chaco-4.5.0/0000755000076600000240000000000012426466422013365 5ustar jrocherstaff00000000000000chaco-4.5.0/chaco/0000755000076600000240000000000012426466422014442 5ustar jrocherstaff00000000000000chaco-4.5.0/chaco/__init__.py0000644000076600000240000000035612426464453016561 0ustar jrocherstaff00000000000000# Copyright (c) 2005-2014 by Enthought, Inc. # All rights reserved. """ Two-dimensional plotting application toolkit. Part of the Chaco project of the Enthought Tool Suite. """ __version__ = '4.5.0' __requires__ = [ 'enable', ] chaco-4.5.0/chaco/_cython_speedups.c0000644000076600000240000126617012426452312020167 0ustar jrocherstaff00000000000000/* Generated by Cython 0.16 on Tue May 21 11:22:41 2013 */ #define PY_SSIZE_T_CLEAN #include "Python.h" #ifndef Py_PYTHON_H #error Python headers needed to compile C extensions, please install development version of Python. #elif PY_VERSION_HEX < 0x02040000 #error Cython requires Python 2.4+. #else #include /* For offsetof */ #ifndef offsetof #define offsetof(type, member) ( (size_t) & ((type*)0) -> member ) #endif #if !defined(WIN32) && !defined(MS_WINDOWS) #ifndef __stdcall #define __stdcall #endif #ifndef __cdecl #define __cdecl #endif #ifndef __fastcall #define __fastcall #endif #endif #ifndef DL_IMPORT #define DL_IMPORT(t) t #endif #ifndef DL_EXPORT #define DL_EXPORT(t) t #endif #ifndef PY_LONG_LONG #define PY_LONG_LONG LONG_LONG #endif #ifndef Py_HUGE_VAL #define Py_HUGE_VAL HUGE_VAL #endif #ifdef PYPY_VERSION #define CYTHON_COMPILING_IN_PYPY 1 #define CYTHON_COMPILING_IN_CPYTHON 0 #else #define CYTHON_COMPILING_IN_PYPY 0 #define CYTHON_COMPILING_IN_CPYTHON 1 #endif #if CYTHON_COMPILING_IN_PYPY #define __Pyx_PyCFunction_Call PyObject_Call #else #define __Pyx_PyCFunction_Call PyCFunction_Call #endif #if PY_VERSION_HEX < 0x02050000 typedef int Py_ssize_t; #define PY_SSIZE_T_MAX INT_MAX #define PY_SSIZE_T_MIN INT_MIN #define PY_FORMAT_SIZE_T "" #define PyInt_FromSsize_t(z) PyInt_FromLong(z) #define PyInt_AsSsize_t(o) __Pyx_PyInt_AsInt(o) #define PyNumber_Index(o) PyNumber_Int(o) #define PyIndex_Check(o) PyNumber_Check(o) #define PyErr_WarnEx(category, message, stacklevel) PyErr_Warn(category, message) #define __PYX_BUILD_PY_SSIZE_T "i" #else #define __PYX_BUILD_PY_SSIZE_T "n" #endif #if PY_VERSION_HEX < 0x02060000 #define Py_REFCNT(ob) (((PyObject*)(ob))->ob_refcnt) #define Py_TYPE(ob) (((PyObject*)(ob))->ob_type) #define Py_SIZE(ob) (((PyVarObject*)(ob))->ob_size) #define PyVarObject_HEAD_INIT(type, size) \ PyObject_HEAD_INIT(type) size, #define PyType_Modified(t) typedef struct { void *buf; PyObject *obj; Py_ssize_t len; Py_ssize_t itemsize; int readonly; int ndim; char *format; Py_ssize_t *shape; Py_ssize_t *strides; Py_ssize_t *suboffsets; void *internal; } Py_buffer; #define PyBUF_SIMPLE 0 #define PyBUF_WRITABLE 0x0001 #define PyBUF_FORMAT 0x0004 #define PyBUF_ND 0x0008 #define PyBUF_STRIDES (0x0010 | PyBUF_ND) #define PyBUF_C_CONTIGUOUS (0x0020 | PyBUF_STRIDES) #define PyBUF_F_CONTIGUOUS (0x0040 | PyBUF_STRIDES) #define PyBUF_ANY_CONTIGUOUS (0x0080 | PyBUF_STRIDES) #define PyBUF_INDIRECT (0x0100 | PyBUF_STRIDES) #define PyBUF_RECORDS (PyBUF_STRIDES | PyBUF_FORMAT | PyBUF_WRITABLE) #define PyBUF_FULL (PyBUF_INDIRECT | PyBUF_FORMAT | PyBUF_WRITABLE) typedef int (*getbufferproc)(PyObject *, Py_buffer *, int); typedef void (*releasebufferproc)(PyObject *, Py_buffer *); #endif #if PY_MAJOR_VERSION < 3 #define __Pyx_BUILTIN_MODULE_NAME "__builtin__" #define __Pyx_PyCode_New(a, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos) \ PyCode_New(a, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos) #else #define __Pyx_BUILTIN_MODULE_NAME "builtins" #define __Pyx_PyCode_New(a, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos) \ PyCode_New(a, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos) #endif #if PY_MAJOR_VERSION < 3 && PY_MINOR_VERSION < 6 #define PyUnicode_FromString(s) PyUnicode_Decode(s, strlen(s), "UTF-8", "strict") #endif #if PY_MAJOR_VERSION >= 3 #define Py_TPFLAGS_CHECKTYPES 0 #define Py_TPFLAGS_HAVE_INDEX 0 #endif #if (PY_VERSION_HEX < 0x02060000) || (PY_MAJOR_VERSION >= 3) #define Py_TPFLAGS_HAVE_NEWBUFFER 0 #endif #if PY_VERSION_HEX > 0x03030000 && defined(PyUnicode_GET_LENGTH) #define CYTHON_PEP393_ENABLED 1 #define __Pyx_PyUnicode_GET_LENGTH(u) PyUnicode_GET_LENGTH(u) #define __Pyx_PyUnicode_READ_CHAR(u, i) PyUnicode_READ_CHAR(u, i) #else #define CYTHON_PEP393_ENABLED 0 #define __Pyx_PyUnicode_GET_LENGTH(u) PyUnicode_GET_SIZE(u) #define __Pyx_PyUnicode_READ_CHAR(u, i) ((Py_UCS4)(PyUnicode_AS_UNICODE(u)[i])) #endif #if PY_MAJOR_VERSION >= 3 #define PyBaseString_Type PyUnicode_Type #define PyStringObject PyUnicodeObject #define PyString_Type PyUnicode_Type #define PyString_Check PyUnicode_Check #define PyString_CheckExact PyUnicode_CheckExact #endif #if PY_VERSION_HEX < 0x02060000 #define PyBytesObject PyStringObject #define PyBytes_Type PyString_Type #define PyBytes_Check PyString_Check #define PyBytes_CheckExact PyString_CheckExact #define PyBytes_FromString PyString_FromString #define PyBytes_FromStringAndSize PyString_FromStringAndSize #define PyBytes_FromFormat PyString_FromFormat #define PyBytes_DecodeEscape PyString_DecodeEscape #define PyBytes_AsString PyString_AsString #define PyBytes_AsStringAndSize PyString_AsStringAndSize #define PyBytes_Size PyString_Size #define PyBytes_AS_STRING PyString_AS_STRING #define PyBytes_GET_SIZE PyString_GET_SIZE #define PyBytes_Repr PyString_Repr #define PyBytes_Concat PyString_Concat #define PyBytes_ConcatAndDel PyString_ConcatAndDel #endif #if PY_VERSION_HEX < 0x02060000 #define PySet_Check(obj) PyObject_TypeCheck(obj, &PySet_Type) #define PyFrozenSet_Check(obj) PyObject_TypeCheck(obj, &PyFrozenSet_Type) #endif #ifndef PySet_CheckExact #define PySet_CheckExact(obj) (Py_TYPE(obj) == &PySet_Type) #endif #define __Pyx_TypeCheck(obj, type) PyObject_TypeCheck(obj, (PyTypeObject *)type) #if PY_MAJOR_VERSION >= 3 #define PyIntObject PyLongObject #define PyInt_Type PyLong_Type #define PyInt_Check(op) PyLong_Check(op) #define PyInt_CheckExact(op) PyLong_CheckExact(op) #define PyInt_FromString PyLong_FromString #define PyInt_FromUnicode PyLong_FromUnicode #define PyInt_FromLong PyLong_FromLong #define PyInt_FromSize_t PyLong_FromSize_t #define PyInt_FromSsize_t PyLong_FromSsize_t #define PyInt_AsLong PyLong_AsLong #define PyInt_AS_LONG PyLong_AS_LONG #define PyInt_AsSsize_t PyLong_AsSsize_t #define PyInt_AsUnsignedLongMask PyLong_AsUnsignedLongMask #define PyInt_AsUnsignedLongLongMask PyLong_AsUnsignedLongLongMask #endif #if PY_MAJOR_VERSION >= 3 #define PyBoolObject PyLongObject #endif #if PY_VERSION_HEX < 0x03020000 typedef long Py_hash_t; #define __Pyx_PyInt_FromHash_t PyInt_FromLong #define __Pyx_PyInt_AsHash_t PyInt_AsLong #else #define __Pyx_PyInt_FromHash_t PyInt_FromSsize_t #define __Pyx_PyInt_AsHash_t PyInt_AsSsize_t #endif #if (PY_MAJOR_VERSION < 3) || (PY_VERSION_HEX >= 0x03010300) #define __Pyx_PySequence_GetSlice(obj, a, b) PySequence_GetSlice(obj, a, b) #define __Pyx_PySequence_SetSlice(obj, a, b, value) PySequence_SetSlice(obj, a, b, value) #define __Pyx_PySequence_DelSlice(obj, a, b) PySequence_DelSlice(obj, a, b) #else #define __Pyx_PySequence_GetSlice(obj, a, b) (unlikely(!(obj)) ? \ (PyErr_SetString(PyExc_SystemError, "null argument to internal routine"), (PyObject*)0) : \ (likely((obj)->ob_type->tp_as_mapping) ? (PySequence_GetSlice(obj, a, b)) : \ (PyErr_Format(PyExc_TypeError, "'%.200s' object is unsliceable", (obj)->ob_type->tp_name), (PyObject*)0))) #define __Pyx_PySequence_SetSlice(obj, a, b, value) (unlikely(!(obj)) ? \ (PyErr_SetString(PyExc_SystemError, "null argument to internal routine"), -1) : \ (likely((obj)->ob_type->tp_as_mapping) ? (PySequence_SetSlice(obj, a, b, value)) : \ (PyErr_Format(PyExc_TypeError, "'%.200s' object doesn't support slice assignment", (obj)->ob_type->tp_name), -1))) #define __Pyx_PySequence_DelSlice(obj, a, b) (unlikely(!(obj)) ? \ (PyErr_SetString(PyExc_SystemError, "null argument to internal routine"), -1) : \ (likely((obj)->ob_type->tp_as_mapping) ? (PySequence_DelSlice(obj, a, b)) : \ (PyErr_Format(PyExc_TypeError, "'%.200s' object doesn't support slice deletion", (obj)->ob_type->tp_name), -1))) #endif #if PY_MAJOR_VERSION >= 3 #define PyMethod_New(func, self, klass) ((self) ? PyMethod_New(func, self) : PyInstanceMethod_New(func)) #endif #if PY_VERSION_HEX < 0x02050000 #define __Pyx_GetAttrString(o,n) PyObject_GetAttrString((o),((char *)(n))) #define __Pyx_SetAttrString(o,n,a) PyObject_SetAttrString((o),((char *)(n)),(a)) #define __Pyx_DelAttrString(o,n) PyObject_DelAttrString((o),((char *)(n))) #else #define __Pyx_GetAttrString(o,n) PyObject_GetAttrString((o),(n)) #define __Pyx_SetAttrString(o,n,a) PyObject_SetAttrString((o),(n),(a)) #define __Pyx_DelAttrString(o,n) PyObject_DelAttrString((o),(n)) #endif #if PY_VERSION_HEX < 0x02050000 #define __Pyx_NAMESTR(n) ((char *)(n)) #define __Pyx_DOCSTR(n) ((char *)(n)) #else #define __Pyx_NAMESTR(n) (n) #define __Pyx_DOCSTR(n) (n) #endif #if PY_MAJOR_VERSION >= 3 #define __Pyx_PyNumber_Divide(x,y) PyNumber_TrueDivide(x,y) #define __Pyx_PyNumber_InPlaceDivide(x,y) PyNumber_InPlaceTrueDivide(x,y) #else #define __Pyx_PyNumber_Divide(x,y) PyNumber_Divide(x,y) #define __Pyx_PyNumber_InPlaceDivide(x,y) PyNumber_InPlaceDivide(x,y) #endif #ifndef __PYX_EXTERN_C #ifdef __cplusplus #define __PYX_EXTERN_C extern "C" #else #define __PYX_EXTERN_C extern #endif #endif #if defined(WIN32) || defined(MS_WINDOWS) #define _USE_MATH_DEFINES #endif #include #define __PYX_HAVE__chaco___cython_speedups #define __PYX_HAVE_API__chaco___cython_speedups #include "stdio.h" #include "stdlib.h" #include "numpy/arrayobject.h" #include "numpy/ufuncobject.h" #include "_isnan.h" #ifdef _OPENMP #include #endif /* _OPENMP */ #ifdef PYREX_WITHOUT_ASSERTIONS #define CYTHON_WITHOUT_ASSERTIONS #endif /* inline attribute */ #ifndef CYTHON_INLINE #if defined(__GNUC__) #define CYTHON_INLINE __inline__ #elif defined(_MSC_VER) #define CYTHON_INLINE __inline #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L #define CYTHON_INLINE inline #else #define CYTHON_INLINE #endif #endif /* unused attribute */ #ifndef CYTHON_UNUSED # if defined(__GNUC__) # if !(defined(__cplusplus)) || (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) # define CYTHON_UNUSED __attribute__ ((__unused__)) # else # define CYTHON_UNUSED # endif # elif defined(__ICC) || (defined(__INTEL_COMPILER) && !defined(_MSC_VER)) # define CYTHON_UNUSED __attribute__ ((__unused__)) # else # define CYTHON_UNUSED # endif #endif typedef struct {PyObject **p; char *s; const long n; const char* encoding; const char is_unicode; const char is_str; const char intern; } __Pyx_StringTabEntry; /*proto*/ /* Type Conversion Predeclarations */ #define __Pyx_PyBytes_FromUString(s) PyBytes_FromString((char*)s) #define __Pyx_PyBytes_AsUString(s) ((unsigned char*) PyBytes_AsString(s)) #define __Pyx_Owned_Py_None(b) (Py_INCREF(Py_None), Py_None) #define __Pyx_PyBool_FromLong(b) ((b) ? (Py_INCREF(Py_True), Py_True) : (Py_INCREF(Py_False), Py_False)) static CYTHON_INLINE int __Pyx_PyObject_IsTrue(PyObject*); static CYTHON_INLINE PyObject* __Pyx_PyNumber_Int(PyObject* x); static CYTHON_INLINE Py_ssize_t __Pyx_PyIndex_AsSsize_t(PyObject*); static CYTHON_INLINE PyObject * __Pyx_PyInt_FromSize_t(size_t); static CYTHON_INLINE size_t __Pyx_PyInt_AsSize_t(PyObject*); #define __pyx_PyFloat_AsDouble(x) (PyFloat_CheckExact(x) ? PyFloat_AS_DOUBLE(x) : PyFloat_AsDouble(x)) #define __pyx_PyFloat_AsFloat(x) ((float) __pyx_PyFloat_AsDouble(x)) #ifdef __GNUC__ /* Test for GCC > 2.95 */ #if __GNUC__ > 2 || (__GNUC__ == 2 && (__GNUC_MINOR__ > 95)) #define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0) #else /* __GNUC__ > 2 ... */ #define likely(x) (x) #define unlikely(x) (x) #endif /* __GNUC__ > 2 ... */ #else /* __GNUC__ */ #define likely(x) (x) #define unlikely(x) (x) #endif /* __GNUC__ */ static PyObject *__pyx_m; static PyObject *__pyx_b; static PyObject *__pyx_empty_tuple; static PyObject *__pyx_empty_bytes; static int __pyx_lineno; static int __pyx_clineno = 0; static const char * __pyx_cfilenm= __FILE__; static const char *__pyx_filename; #if !defined(CYTHON_CCOMPLEX) #if defined(__cplusplus) #define CYTHON_CCOMPLEX 1 #elif defined(_Complex_I) #define CYTHON_CCOMPLEX 1 #else #define CYTHON_CCOMPLEX 0 #endif #endif #if CYTHON_CCOMPLEX #ifdef __cplusplus #include #else #include #endif #endif #if CYTHON_CCOMPLEX && !defined(__cplusplus) && defined(__sun__) && defined(__GNUC__) #undef _Complex_I #define _Complex_I 1.0fj #endif static const char *__pyx_f[] = { "_cython_speedups.pyx", "numpy.pxd", }; #define IS_UNSIGNED(type) (((type) -1) > 0) struct __Pyx_StructField_; #define __PYX_BUF_FLAGS_PACKED_STRUCT (1 << 0) typedef struct { const char* name; /* for error messages only */ struct __Pyx_StructField_* fields; size_t size; /* sizeof(type) */ size_t arraysize[8]; /* length of array in each dimension */ int ndim; char typegroup; /* _R_eal, _C_omplex, Signed _I_nt, _U_nsigned int, _S_truct, _P_ointer, _O_bject */ char is_unsigned; int flags; } __Pyx_TypeInfo; typedef struct __Pyx_StructField_ { __Pyx_TypeInfo* type; const char* name; size_t offset; } __Pyx_StructField; typedef struct { __Pyx_StructField* field; size_t parent_offset; } __Pyx_BufFmt_StackElem; typedef struct { __Pyx_StructField root; __Pyx_BufFmt_StackElem* head; size_t fmt_offset; size_t new_count, enc_count; size_t struct_alignment; int is_complex; char enc_type; char new_packmode; char enc_packmode; char is_valid_array; } __Pyx_BufFmt_Context; /* "numpy.pxd":722 * # in Cython to enable them only on the right systems. * * ctypedef npy_int8 int8_t # <<<<<<<<<<<<<< * ctypedef npy_int16 int16_t * ctypedef npy_int32 int32_t */ typedef npy_int8 __pyx_t_5numpy_int8_t; /* "numpy.pxd":723 * * ctypedef npy_int8 int8_t * ctypedef npy_int16 int16_t # <<<<<<<<<<<<<< * ctypedef npy_int32 int32_t * ctypedef npy_int64 int64_t */ typedef npy_int16 __pyx_t_5numpy_int16_t; /* "numpy.pxd":724 * ctypedef npy_int8 int8_t * ctypedef npy_int16 int16_t * ctypedef npy_int32 int32_t # <<<<<<<<<<<<<< * ctypedef npy_int64 int64_t * #ctypedef npy_int96 int96_t */ typedef npy_int32 __pyx_t_5numpy_int32_t; /* "numpy.pxd":725 * ctypedef npy_int16 int16_t * ctypedef npy_int32 int32_t * ctypedef npy_int64 int64_t # <<<<<<<<<<<<<< * #ctypedef npy_int96 int96_t * #ctypedef npy_int128 int128_t */ typedef npy_int64 __pyx_t_5numpy_int64_t; /* "numpy.pxd":729 * #ctypedef npy_int128 int128_t * * ctypedef npy_uint8 uint8_t # <<<<<<<<<<<<<< * ctypedef npy_uint16 uint16_t * ctypedef npy_uint32 uint32_t */ typedef npy_uint8 __pyx_t_5numpy_uint8_t; /* "numpy.pxd":730 * * ctypedef npy_uint8 uint8_t * ctypedef npy_uint16 uint16_t # <<<<<<<<<<<<<< * ctypedef npy_uint32 uint32_t * ctypedef npy_uint64 uint64_t */ typedef npy_uint16 __pyx_t_5numpy_uint16_t; /* "numpy.pxd":731 * ctypedef npy_uint8 uint8_t * ctypedef npy_uint16 uint16_t * ctypedef npy_uint32 uint32_t # <<<<<<<<<<<<<< * ctypedef npy_uint64 uint64_t * #ctypedef npy_uint96 uint96_t */ typedef npy_uint32 __pyx_t_5numpy_uint32_t; /* "numpy.pxd":732 * ctypedef npy_uint16 uint16_t * ctypedef npy_uint32 uint32_t * ctypedef npy_uint64 uint64_t # <<<<<<<<<<<<<< * #ctypedef npy_uint96 uint96_t * #ctypedef npy_uint128 uint128_t */ typedef npy_uint64 __pyx_t_5numpy_uint64_t; /* "numpy.pxd":736 * #ctypedef npy_uint128 uint128_t * * ctypedef npy_float32 float32_t # <<<<<<<<<<<<<< * ctypedef npy_float64 float64_t * #ctypedef npy_float80 float80_t */ typedef npy_float32 __pyx_t_5numpy_float32_t; /* "numpy.pxd":737 * * ctypedef npy_float32 float32_t * ctypedef npy_float64 float64_t # <<<<<<<<<<<<<< * #ctypedef npy_float80 float80_t * #ctypedef npy_float128 float128_t */ typedef npy_float64 __pyx_t_5numpy_float64_t; /* "numpy.pxd":746 * # The int types are mapped a bit surprising -- * # numpy.int corresponds to 'l' and numpy.long to 'q' * ctypedef npy_long int_t # <<<<<<<<<<<<<< * ctypedef npy_longlong long_t * ctypedef npy_longlong longlong_t */ typedef npy_long __pyx_t_5numpy_int_t; /* "numpy.pxd":747 * # numpy.int corresponds to 'l' and numpy.long to 'q' * ctypedef npy_long int_t * ctypedef npy_longlong long_t # <<<<<<<<<<<<<< * ctypedef npy_longlong longlong_t * */ typedef npy_longlong __pyx_t_5numpy_long_t; /* "numpy.pxd":748 * ctypedef npy_long int_t * ctypedef npy_longlong long_t * ctypedef npy_longlong longlong_t # <<<<<<<<<<<<<< * * ctypedef npy_ulong uint_t */ typedef npy_longlong __pyx_t_5numpy_longlong_t; /* "numpy.pxd":750 * ctypedef npy_longlong longlong_t * * ctypedef npy_ulong uint_t # <<<<<<<<<<<<<< * ctypedef npy_ulonglong ulong_t * ctypedef npy_ulonglong ulonglong_t */ typedef npy_ulong __pyx_t_5numpy_uint_t; /* "numpy.pxd":751 * * ctypedef npy_ulong uint_t * ctypedef npy_ulonglong ulong_t # <<<<<<<<<<<<<< * ctypedef npy_ulonglong ulonglong_t * */ typedef npy_ulonglong __pyx_t_5numpy_ulong_t; /* "numpy.pxd":752 * ctypedef npy_ulong uint_t * ctypedef npy_ulonglong ulong_t * ctypedef npy_ulonglong ulonglong_t # <<<<<<<<<<<<<< * * ctypedef npy_intp intp_t */ typedef npy_ulonglong __pyx_t_5numpy_ulonglong_t; /* "numpy.pxd":754 * ctypedef npy_ulonglong ulonglong_t * * ctypedef npy_intp intp_t # <<<<<<<<<<<<<< * ctypedef npy_uintp uintp_t * */ typedef npy_intp __pyx_t_5numpy_intp_t; /* "numpy.pxd":755 * * ctypedef npy_intp intp_t * ctypedef npy_uintp uintp_t # <<<<<<<<<<<<<< * * ctypedef npy_double float_t */ typedef npy_uintp __pyx_t_5numpy_uintp_t; /* "numpy.pxd":757 * ctypedef npy_uintp uintp_t * * ctypedef npy_double float_t # <<<<<<<<<<<<<< * ctypedef npy_double double_t * ctypedef npy_longdouble longdouble_t */ typedef npy_double __pyx_t_5numpy_float_t; /* "numpy.pxd":758 * * ctypedef npy_double float_t * ctypedef npy_double double_t # <<<<<<<<<<<<<< * ctypedef npy_longdouble longdouble_t * */ typedef npy_double __pyx_t_5numpy_double_t; /* "numpy.pxd":759 * ctypedef npy_double float_t * ctypedef npy_double double_t * ctypedef npy_longdouble longdouble_t # <<<<<<<<<<<<<< * * ctypedef npy_cfloat cfloat_t */ typedef npy_longdouble __pyx_t_5numpy_longdouble_t; #if CYTHON_CCOMPLEX #ifdef __cplusplus typedef ::std::complex< float > __pyx_t_float_complex; #else typedef float _Complex __pyx_t_float_complex; #endif #else typedef struct { float real, imag; } __pyx_t_float_complex; #endif #if CYTHON_CCOMPLEX #ifdef __cplusplus typedef ::std::complex< double > __pyx_t_double_complex; #else typedef double _Complex __pyx_t_double_complex; #endif #else typedef struct { double real, imag; } __pyx_t_double_complex; #endif /*--- Type declarations ---*/ /* "numpy.pxd":761 * ctypedef npy_longdouble longdouble_t * * ctypedef npy_cfloat cfloat_t # <<<<<<<<<<<<<< * ctypedef npy_cdouble cdouble_t * ctypedef npy_clongdouble clongdouble_t */ typedef npy_cfloat __pyx_t_5numpy_cfloat_t; /* "numpy.pxd":762 * * ctypedef npy_cfloat cfloat_t * ctypedef npy_cdouble cdouble_t # <<<<<<<<<<<<<< * ctypedef npy_clongdouble clongdouble_t * */ typedef npy_cdouble __pyx_t_5numpy_cdouble_t; /* "numpy.pxd":763 * ctypedef npy_cfloat cfloat_t * ctypedef npy_cdouble cdouble_t * ctypedef npy_clongdouble clongdouble_t # <<<<<<<<<<<<<< * * ctypedef npy_cdouble complex_t */ typedef npy_clongdouble __pyx_t_5numpy_clongdouble_t; /* "numpy.pxd":765 * ctypedef npy_clongdouble clongdouble_t * * ctypedef npy_cdouble complex_t # <<<<<<<<<<<<<< * * cdef inline object PyArray_MultiIterNew1(a): */ typedef npy_cdouble __pyx_t_5numpy_complex_t; #ifndef CYTHON_REFNANNY #define CYTHON_REFNANNY 0 #endif #if CYTHON_REFNANNY typedef struct { void (*INCREF)(void*, PyObject*, int); void (*DECREF)(void*, PyObject*, int); void (*GOTREF)(void*, PyObject*, int); void (*GIVEREF)(void*, PyObject*, int); void* (*SetupContext)(const char*, int, const char*); void (*FinishContext)(void**); } __Pyx_RefNannyAPIStruct; static __Pyx_RefNannyAPIStruct *__Pyx_RefNanny = NULL; static __Pyx_RefNannyAPIStruct *__Pyx_RefNannyImportAPI(const char *modname); /*proto*/ #define __Pyx_RefNannyDeclarations void *__pyx_refnanny = NULL; #ifdef WITH_THREAD #define __Pyx_RefNannySetupContext(name, acquire_gil) \ if (acquire_gil) { \ PyGILState_STATE __pyx_gilstate_save = PyGILState_Ensure(); \ __pyx_refnanny = __Pyx_RefNanny->SetupContext((name), __LINE__, __FILE__); \ PyGILState_Release(__pyx_gilstate_save); \ } else { \ __pyx_refnanny = __Pyx_RefNanny->SetupContext((name), __LINE__, __FILE__); \ } #else #define __Pyx_RefNannySetupContext(name, acquire_gil) \ __pyx_refnanny = __Pyx_RefNanny->SetupContext((name), __LINE__, __FILE__) #endif #define __Pyx_RefNannyFinishContext() \ __Pyx_RefNanny->FinishContext(&__pyx_refnanny) #define __Pyx_INCREF(r) __Pyx_RefNanny->INCREF(__pyx_refnanny, (PyObject *)(r), __LINE__) #define __Pyx_DECREF(r) __Pyx_RefNanny->DECREF(__pyx_refnanny, (PyObject *)(r), __LINE__) #define __Pyx_GOTREF(r) __Pyx_RefNanny->GOTREF(__pyx_refnanny, (PyObject *)(r), __LINE__) #define __Pyx_GIVEREF(r) __Pyx_RefNanny->GIVEREF(__pyx_refnanny, (PyObject *)(r), __LINE__) #define __Pyx_XINCREF(r) do { if((r) != NULL) {__Pyx_INCREF(r); }} while(0) #define __Pyx_XDECREF(r) do { if((r) != NULL) {__Pyx_DECREF(r); }} while(0) #define __Pyx_XGOTREF(r) do { if((r) != NULL) {__Pyx_GOTREF(r); }} while(0) #define __Pyx_XGIVEREF(r) do { if((r) != NULL) {__Pyx_GIVEREF(r);}} while(0) #else #define __Pyx_RefNannyDeclarations #define __Pyx_RefNannySetupContext(name, acquire_gil) #define __Pyx_RefNannyFinishContext() #define __Pyx_INCREF(r) Py_INCREF(r) #define __Pyx_DECREF(r) Py_DECREF(r) #define __Pyx_GOTREF(r) #define __Pyx_GIVEREF(r) #define __Pyx_XINCREF(r) Py_XINCREF(r) #define __Pyx_XDECREF(r) Py_XDECREF(r) #define __Pyx_XGOTREF(r) #define __Pyx_XGIVEREF(r) #endif /* CYTHON_REFNANNY */ #define __Pyx_CLEAR(r) do { PyObject* tmp = ((PyObject*)(r)); r = NULL; __Pyx_DECREF(tmp);} while(0) #define __Pyx_XCLEAR(r) do { if((r) != NULL) {PyObject* tmp = ((PyObject*)(r)); r = NULL; __Pyx_DECREF(tmp);}} while(0) static PyObject *__Pyx_GetName(PyObject *dict, PyObject *name); /*proto*/ static void __Pyx_RaiseArgtupleInvalid(const char* func_name, int exact, Py_ssize_t num_min, Py_ssize_t num_max, Py_ssize_t num_found); /*proto*/ static void __Pyx_RaiseDoubleKeywordsError(const char* func_name, PyObject* kw_name); /*proto*/ static int __Pyx_ParseOptionalKeywords(PyObject *kwds, PyObject **argnames[], \ PyObject *kwds2, PyObject *values[], Py_ssize_t num_pos_args, \ const char* function_name); /*proto*/ static int __Pyx_ArgTypeTest(PyObject *obj, PyTypeObject *type, int none_allowed, const char *name, int exact); /*proto*/ static CYTHON_INLINE int __Pyx_GetBufferAndValidate(Py_buffer* buf, PyObject* obj, __Pyx_TypeInfo* dtype, int flags, int nd, int cast, __Pyx_BufFmt_StackElem* stack); static CYTHON_INLINE void __Pyx_SafeReleaseBuffer(Py_buffer* info); static CYTHON_INLINE int __Pyx_TypeTest(PyObject *obj, PyTypeObject *type); /*proto*/ #define __Pyx_BufPtrStrided1d(type, buf, i0, s0) (type)((char*)buf + i0 * s0) #define __Pyx_BufPtrStrided2d(type, buf, i0, s0, i1, s1) (type)((char*)buf + i0 * s0 + i1 * s1) static CYTHON_INLINE void __Pyx_ErrRestore(PyObject *type, PyObject *value, PyObject *tb); /*proto*/ static CYTHON_INLINE void __Pyx_ErrFetch(PyObject **type, PyObject **value, PyObject **tb); /*proto*/ static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Generic(PyObject *o, PyObject* j) { PyObject *r; if (!j) return NULL; r = PyObject_GetItem(o, j); Py_DECREF(j); return r; } #define __Pyx_GetItemInt_List(o, i, size, to_py_func) (((size) <= sizeof(Py_ssize_t)) ? \ __Pyx_GetItemInt_List_Fast(o, i) : \ __Pyx_GetItemInt_Generic(o, to_py_func(i))) static CYTHON_INLINE PyObject *__Pyx_GetItemInt_List_Fast(PyObject *o, Py_ssize_t i) { if (likely(o != Py_None)) { if (likely((0 <= i) & (i < PyList_GET_SIZE(o)))) { PyObject *r = PyList_GET_ITEM(o, i); Py_INCREF(r); return r; } else if ((-PyList_GET_SIZE(o) <= i) & (i < 0)) { PyObject *r = PyList_GET_ITEM(o, PyList_GET_SIZE(o) + i); Py_INCREF(r); return r; } } return __Pyx_GetItemInt_Generic(o, PyInt_FromSsize_t(i)); } #define __Pyx_GetItemInt_Tuple(o, i, size, to_py_func) (((size) <= sizeof(Py_ssize_t)) ? \ __Pyx_GetItemInt_Tuple_Fast(o, i) : \ __Pyx_GetItemInt_Generic(o, to_py_func(i))) static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Tuple_Fast(PyObject *o, Py_ssize_t i) { if (likely(o != Py_None)) { if (likely((0 <= i) & (i < PyTuple_GET_SIZE(o)))) { PyObject *r = PyTuple_GET_ITEM(o, i); Py_INCREF(r); return r; } else if ((-PyTuple_GET_SIZE(o) <= i) & (i < 0)) { PyObject *r = PyTuple_GET_ITEM(o, PyTuple_GET_SIZE(o) + i); Py_INCREF(r); return r; } } return __Pyx_GetItemInt_Generic(o, PyInt_FromSsize_t(i)); } #define __Pyx_GetItemInt(o, i, size, to_py_func) (((size) <= sizeof(Py_ssize_t)) ? \ __Pyx_GetItemInt_Fast(o, i) : \ __Pyx_GetItemInt_Generic(o, to_py_func(i))) static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Fast(PyObject *o, Py_ssize_t i) { if (PyList_CheckExact(o)) { Py_ssize_t n = (likely(i >= 0)) ? i : i + PyList_GET_SIZE(o); if (likely((n >= 0) & (n < PyList_GET_SIZE(o)))) { PyObject *r = PyList_GET_ITEM(o, n); Py_INCREF(r); return r; } } else if (PyTuple_CheckExact(o)) { Py_ssize_t n = (likely(i >= 0)) ? i : i + PyTuple_GET_SIZE(o); if (likely((n >= 0) & (n < PyTuple_GET_SIZE(o)))) { PyObject *r = PyTuple_GET_ITEM(o, n); Py_INCREF(r); return r; } } else if (likely(i >= 0)) { PySequenceMethods *m = Py_TYPE(o)->tp_as_sequence; if (likely(m && m->sq_item)) { return m->sq_item(o, i); } } return __Pyx_GetItemInt_Generic(o, PyInt_FromSsize_t(i)); } #define __Pyx_BufPtrStrided3d(type, buf, i0, s0, i1, s1, i2, s2) (type)((char*)buf + i0 * s0 + i1 * s1 + i2 * s2) static void __Pyx_Raise(PyObject *type, PyObject *value, PyObject *tb, PyObject *cause); /*proto*/ static CYTHON_INLINE void __Pyx_RaiseNeedMoreValuesError(Py_ssize_t index); static CYTHON_INLINE void __Pyx_RaiseTooManyValuesError(Py_ssize_t expected); static CYTHON_INLINE void __Pyx_RaiseNoneNotIterableError(void); static void __Pyx_UnpackTupleError(PyObject *, Py_ssize_t index); /*proto*/ typedef struct { Py_ssize_t shape, strides, suboffsets; } __Pyx_Buf_DimInfo; typedef struct { size_t refcount; Py_buffer pybuffer; } __Pyx_Buffer; typedef struct { __Pyx_Buffer *rcbuffer; char *data; __Pyx_Buf_DimInfo diminfo[8]; } __Pyx_LocalBuf_ND; #if PY_MAJOR_VERSION < 3 static int __Pyx_GetBuffer(PyObject *obj, Py_buffer *view, int flags); static void __Pyx_ReleaseBuffer(Py_buffer *view); #else #define __Pyx_GetBuffer PyObject_GetBuffer #define __Pyx_ReleaseBuffer PyBuffer_Release #endif static Py_ssize_t __Pyx_zeros[] = {0, 0, 0, 0, 0, 0, 0, 0}; static Py_ssize_t __Pyx_minusones[] = {-1, -1, -1, -1, -1, -1, -1, -1}; static PyObject *__Pyx_Import(PyObject *name, PyObject *from_list, long level); /*proto*/ #if CYTHON_CCOMPLEX #ifdef __cplusplus #define __Pyx_CREAL(z) ((z).real()) #define __Pyx_CIMAG(z) ((z).imag()) #else #define __Pyx_CREAL(z) (__real__(z)) #define __Pyx_CIMAG(z) (__imag__(z)) #endif #else #define __Pyx_CREAL(z) ((z).real) #define __Pyx_CIMAG(z) ((z).imag) #endif #if defined(_WIN32) && defined(__cplusplus) && CYTHON_CCOMPLEX #define __Pyx_SET_CREAL(z,x) ((z).real(x)) #define __Pyx_SET_CIMAG(z,y) ((z).imag(y)) #else #define __Pyx_SET_CREAL(z,x) __Pyx_CREAL(z) = (x) #define __Pyx_SET_CIMAG(z,y) __Pyx_CIMAG(z) = (y) #endif static CYTHON_INLINE __pyx_t_float_complex __pyx_t_float_complex_from_parts(float, float); #if CYTHON_CCOMPLEX #define __Pyx_c_eqf(a, b) ((a)==(b)) #define __Pyx_c_sumf(a, b) ((a)+(b)) #define __Pyx_c_difff(a, b) ((a)-(b)) #define __Pyx_c_prodf(a, b) ((a)*(b)) #define __Pyx_c_quotf(a, b) ((a)/(b)) #define __Pyx_c_negf(a) (-(a)) #ifdef __cplusplus #define __Pyx_c_is_zerof(z) ((z)==(float)0) #define __Pyx_c_conjf(z) (::std::conj(z)) #if 1 #define __Pyx_c_absf(z) (::std::abs(z)) #define __Pyx_c_powf(a, b) (::std::pow(a, b)) #endif #else #define __Pyx_c_is_zerof(z) ((z)==0) #define __Pyx_c_conjf(z) (conjf(z)) #if 1 #define __Pyx_c_absf(z) (cabsf(z)) #define __Pyx_c_powf(a, b) (cpowf(a, b)) #endif #endif #else static CYTHON_INLINE int __Pyx_c_eqf(__pyx_t_float_complex, __pyx_t_float_complex); static CYTHON_INLINE __pyx_t_float_complex __Pyx_c_sumf(__pyx_t_float_complex, __pyx_t_float_complex); static CYTHON_INLINE __pyx_t_float_complex __Pyx_c_difff(__pyx_t_float_complex, __pyx_t_float_complex); static CYTHON_INLINE __pyx_t_float_complex __Pyx_c_prodf(__pyx_t_float_complex, __pyx_t_float_complex); static CYTHON_INLINE __pyx_t_float_complex __Pyx_c_quotf(__pyx_t_float_complex, __pyx_t_float_complex); static CYTHON_INLINE __pyx_t_float_complex __Pyx_c_negf(__pyx_t_float_complex); static CYTHON_INLINE int __Pyx_c_is_zerof(__pyx_t_float_complex); static CYTHON_INLINE __pyx_t_float_complex __Pyx_c_conjf(__pyx_t_float_complex); #if 1 static CYTHON_INLINE float __Pyx_c_absf(__pyx_t_float_complex); static CYTHON_INLINE __pyx_t_float_complex __Pyx_c_powf(__pyx_t_float_complex, __pyx_t_float_complex); #endif #endif static CYTHON_INLINE __pyx_t_double_complex __pyx_t_double_complex_from_parts(double, double); #if CYTHON_CCOMPLEX #define __Pyx_c_eq(a, b) ((a)==(b)) #define __Pyx_c_sum(a, b) ((a)+(b)) #define __Pyx_c_diff(a, b) ((a)-(b)) #define __Pyx_c_prod(a, b) ((a)*(b)) #define __Pyx_c_quot(a, b) ((a)/(b)) #define __Pyx_c_neg(a) (-(a)) #ifdef __cplusplus #define __Pyx_c_is_zero(z) ((z)==(double)0) #define __Pyx_c_conj(z) (::std::conj(z)) #if 1 #define __Pyx_c_abs(z) (::std::abs(z)) #define __Pyx_c_pow(a, b) (::std::pow(a, b)) #endif #else #define __Pyx_c_is_zero(z) ((z)==0) #define __Pyx_c_conj(z) (conj(z)) #if 1 #define __Pyx_c_abs(z) (cabs(z)) #define __Pyx_c_pow(a, b) (cpow(a, b)) #endif #endif #else static CYTHON_INLINE int __Pyx_c_eq(__pyx_t_double_complex, __pyx_t_double_complex); static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_sum(__pyx_t_double_complex, __pyx_t_double_complex); static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_diff(__pyx_t_double_complex, __pyx_t_double_complex); static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_prod(__pyx_t_double_complex, __pyx_t_double_complex); static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_quot(__pyx_t_double_complex, __pyx_t_double_complex); static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_neg(__pyx_t_double_complex); static CYTHON_INLINE int __Pyx_c_is_zero(__pyx_t_double_complex); static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_conj(__pyx_t_double_complex); #if 1 static CYTHON_INLINE double __Pyx_c_abs(__pyx_t_double_complex); static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_pow(__pyx_t_double_complex, __pyx_t_double_complex); #endif #endif static CYTHON_INLINE unsigned char __Pyx_PyInt_AsUnsignedChar(PyObject *); static CYTHON_INLINE unsigned short __Pyx_PyInt_AsUnsignedShort(PyObject *); static CYTHON_INLINE unsigned int __Pyx_PyInt_AsUnsignedInt(PyObject *); static CYTHON_INLINE char __Pyx_PyInt_AsChar(PyObject *); static CYTHON_INLINE short __Pyx_PyInt_AsShort(PyObject *); static CYTHON_INLINE int __Pyx_PyInt_AsInt(PyObject *); static CYTHON_INLINE signed char __Pyx_PyInt_AsSignedChar(PyObject *); static CYTHON_INLINE signed short __Pyx_PyInt_AsSignedShort(PyObject *); static CYTHON_INLINE signed int __Pyx_PyInt_AsSignedInt(PyObject *); static CYTHON_INLINE int __Pyx_PyInt_AsLongDouble(PyObject *); static CYTHON_INLINE unsigned long __Pyx_PyInt_AsUnsignedLong(PyObject *); static CYTHON_INLINE unsigned PY_LONG_LONG __Pyx_PyInt_AsUnsignedLongLong(PyObject *); static CYTHON_INLINE long __Pyx_PyInt_AsLong(PyObject *); static CYTHON_INLINE PY_LONG_LONG __Pyx_PyInt_AsLongLong(PyObject *); static CYTHON_INLINE signed long __Pyx_PyInt_AsSignedLong(PyObject *); static CYTHON_INLINE signed PY_LONG_LONG __Pyx_PyInt_AsSignedLongLong(PyObject *); static int __Pyx_check_binary_version(void); #if !defined(__Pyx_PyIdentifier_FromString) #if PY_MAJOR_VERSION < 3 #define __Pyx_PyIdentifier_FromString(s) PyString_FromString(s) #else #define __Pyx_PyIdentifier_FromString(s) PyUnicode_FromString(s) #endif #endif static PyTypeObject *__Pyx_ImportType(const char *module_name, const char *class_name, size_t size, int strict); /*proto*/ static PyObject *__Pyx_ImportModule(const char *name); /*proto*/ typedef struct { int code_line; PyCodeObject* code_object; } __Pyx_CodeObjectCacheEntry; struct __Pyx_CodeObjectCache { int count; int max_count; __Pyx_CodeObjectCacheEntry* entries; }; static struct __Pyx_CodeObjectCache __pyx_code_cache = {0,0,NULL}; static int __pyx_bisect_code_objects(__Pyx_CodeObjectCacheEntry* entries, int count, int code_line); static PyCodeObject *__pyx_find_code_object(int code_line); static void __pyx_insert_code_object(int code_line, PyCodeObject* code_object); static void __Pyx_AddTraceback(const char *funcname, int c_line, int py_line, const char *filename); /*proto*/ static int __Pyx_InitStrings(__Pyx_StringTabEntry *t); /*proto*/ /* Module declarations from 'cpython.buffer' */ /* Module declarations from 'cpython.ref' */ /* Module declarations from 'libc.stdio' */ /* Module declarations from 'cpython.object' */ /* Module declarations from 'libc.stdlib' */ /* Module declarations from 'numpy' */ /* Module declarations from 'numpy' */ static PyTypeObject *__pyx_ptype_5numpy_dtype = 0; static PyTypeObject *__pyx_ptype_5numpy_flatiter = 0; static PyTypeObject *__pyx_ptype_5numpy_broadcast = 0; static PyTypeObject *__pyx_ptype_5numpy_ndarray = 0; static PyTypeObject *__pyx_ptype_5numpy_ufunc = 0; static CYTHON_INLINE char *__pyx_f_5numpy__util_dtypestring(PyArray_Descr *, char *, char *, int *); /*proto*/ /* Module declarations from 'cython' */ /* Module declarations from 'chaco._cython_speedups' */ static CYTHON_INLINE float __pyx_f_5chaco_16_cython_speedups_float_max(float, float); /*proto*/ static CYTHON_INLINE float __pyx_f_5chaco_16_cython_speedups_float_min(float, float); /*proto*/ static __Pyx_TypeInfo __Pyx_TypeInfo_nn___pyx_t_5numpy_float32_t = { "float32_t", NULL, sizeof(__pyx_t_5numpy_float32_t), { 0 }, 0, 'R', 0, 0 }; static __Pyx_TypeInfo __Pyx_TypeInfo_nn___pyx_t_5numpy_uint8_t = { "uint8_t", NULL, sizeof(__pyx_t_5numpy_uint8_t), { 0 }, 0, 'U', IS_UNSIGNED(__pyx_t_5numpy_uint8_t), 0 }; #define __Pyx_MODULE_NAME "chaco._cython_speedups" int __pyx_module_is_main_chaco___cython_speedups = 0; /* Implementation of 'chaco._cython_speedups' */ static PyObject *__pyx_builtin_range; static PyObject *__pyx_builtin_ValueError; static PyObject *__pyx_builtin_RuntimeError; static PyObject *__pyx_pf_5chaco_16_cython_speedups_map_colors(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_data_array, int __pyx_v_steps, float __pyx_v_low, float __pyx_v_high, PyArrayObject *__pyx_v_red_lut, PyArrayObject *__pyx_v_green_lut, PyArrayObject *__pyx_v_blue_lut, PyArrayObject *__pyx_v_alpha_lut); /* proto */ static PyObject *__pyx_pf_5chaco_16_cython_speedups_2map_colors_uint8(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_data_array, int __pyx_v_steps, float __pyx_v_low, float __pyx_v_high, PyArrayObject *__pyx_v_red_lut, PyArrayObject *__pyx_v_green_lut, PyArrayObject *__pyx_v_blue_lut, PyArrayObject *__pyx_v_alpha_lut); /* proto */ static PyObject *__pyx_pf_5chaco_16_cython_speedups_4apply_selection_fade(CYTHON_UNUSED PyObject *__pyx_self, PyArrayObject *__pyx_v_mapped_image, PyObject *__pyx_v_mask, PyObject *__pyx_v_fade_alpha, PyObject *__pyx_v_fade_background); /* proto */ static int __pyx_pf_5numpy_7ndarray___getbuffer__(PyArrayObject *__pyx_v_self, Py_buffer *__pyx_v_info, int __pyx_v_flags); /* proto */ static void __pyx_pf_5numpy_7ndarray_2__releasebuffer__(PyArrayObject *__pyx_v_self, Py_buffer *__pyx_v_info); /* proto */ static char __pyx_k_5[] = "ndarray is not C contiguous"; static char __pyx_k_7[] = "ndarray is not Fortran contiguous"; static char __pyx_k_9[] = "Non-native byte order not supported"; static char __pyx_k_11[] = "unknown dtype code in numpy.pxd (%d)"; static char __pyx_k_12[] = "Format string allocated too short, see comment in numpy.pxd"; static char __pyx_k_15[] = "Format string allocated too short."; static char __pyx_k_19[] = "/Users/jwiggins/source/ets/chaco/chaco/_cython_speedups.pyx"; static char __pyx_k_20[] = "chaco._cython_speedups"; static char __pyx_k_25[] = "apply_selection_fade"; static char __pyx_k__B[] = "B"; static char __pyx_k__H[] = "H"; static char __pyx_k__I[] = "I"; static char __pyx_k__L[] = "L"; static char __pyx_k__M[] = "M"; static char __pyx_k__N[] = "N"; static char __pyx_k__O[] = "O"; static char __pyx_k__Q[] = "Q"; static char __pyx_k__b[] = "b"; static char __pyx_k__d[] = "d"; static char __pyx_k__f[] = "f"; static char __pyx_k__g[] = "g"; static char __pyx_k__h[] = "h"; static char __pyx_k__i[] = "i"; static char __pyx_k__j[] = "j"; static char __pyx_k__l[] = "l"; static char __pyx_k__q[] = "q"; static char __pyx_k__Zd[] = "Zd"; static char __pyx_k__Zf[] = "Zf"; static char __pyx_k__Zg[] = "Zg"; static char __pyx_k__np[] = "np"; static char __pyx_k__idx[] = "idx"; static char __pyx_k__low[] = "low"; static char __pyx_k__red[] = "red"; static char __pyx_k__sys[] = "sys"; static char __pyx_k__bg_b[] = "bg_b"; static char __pyx_k__bg_g[] = "bg_g"; static char __pyx_k__bg_r[] = "bg_r"; static char __pyx_k__blue[] = "blue"; static char __pyx_k__fade[] = "fade"; static char __pyx_k__flat[] = "flat"; static char __pyx_k__high[] = "high"; static char __pyx_k__mask[] = "mask"; static char __pyx_k__rgba[] = "rgba"; static char __pyx_k__size[] = "size"; static char __pyx_k__view[] = "view"; static char __pyx_k__alpha[] = "alpha"; static char __pyx_k__empty[] = "empty"; static char __pyx_k__green[] = "green"; static char __pyx_k__isinf[] = "isinf"; static char __pyx_k__numpy[] = "numpy"; static char __pyx_k__range[] = "range"; static char __pyx_k__shape[] = "shape"; static char __pyx_k__steps[] = "steps"; static char __pyx_k__uint8[] = "uint8"; static char __pyx_k__i8mask[] = "i8mask"; static char __pyx_k__ialpha[] = "ialpha"; static char __pyx_k__float32[] = "float32"; static char __pyx_k__red_lut[] = "red_lut"; static char __pyx_k__reshape[] = "reshape"; static char __pyx_k____main__[] = "__main__"; static char __pyx_k____test__[] = "__test__"; static char __pyx_k__blue_lut[] = "blue_lut"; static char __pyx_k__alpha_lut[] = "alpha_lut"; static char __pyx_k__green_lut[] = "green_lut"; static char __pyx_k__norm_data[] = "norm_data"; static char __pyx_k__ValueError[] = "ValueError"; static char __pyx_k__data_array[] = "data_array"; static char __pyx_k__fade_alpha[] = "fade_alpha"; static char __pyx_k__map_colors[] = "map_colors"; static char __pyx_k__norm_value[] = "norm_value"; static char __pyx_k__range_diff[] = "range_diff"; static char __pyx_k__RuntimeError[] = "RuntimeError"; static char __pyx_k__mapped_image[] = "mapped_image"; static char __pyx_k__fade_background[] = "fade_background"; static char __pyx_k__map_colors_uint8[] = "map_colors_uint8"; static PyObject *__pyx_kp_u_11; static PyObject *__pyx_kp_u_12; static PyObject *__pyx_kp_u_15; static PyObject *__pyx_kp_s_19; static PyObject *__pyx_n_s_20; static PyObject *__pyx_n_s_25; static PyObject *__pyx_kp_u_5; static PyObject *__pyx_kp_u_7; static PyObject *__pyx_kp_u_9; static PyObject *__pyx_n_s__M; static PyObject *__pyx_n_s__N; static PyObject *__pyx_n_s__RuntimeError; static PyObject *__pyx_n_s__ValueError; static PyObject *__pyx_n_s____main__; static PyObject *__pyx_n_s____test__; static PyObject *__pyx_n_s__alpha; static PyObject *__pyx_n_s__alpha_lut; static PyObject *__pyx_n_s__bg_b; static PyObject *__pyx_n_s__bg_g; static PyObject *__pyx_n_s__bg_r; static PyObject *__pyx_n_s__blue; static PyObject *__pyx_n_s__blue_lut; static PyObject *__pyx_n_s__data_array; static PyObject *__pyx_n_s__empty; static PyObject *__pyx_n_s__fade; static PyObject *__pyx_n_s__fade_alpha; static PyObject *__pyx_n_s__fade_background; static PyObject *__pyx_n_s__flat; static PyObject *__pyx_n_s__float32; static PyObject *__pyx_n_s__green; static PyObject *__pyx_n_s__green_lut; static PyObject *__pyx_n_s__high; static PyObject *__pyx_n_s__i; static PyObject *__pyx_n_s__i8mask; static PyObject *__pyx_n_s__ialpha; static PyObject *__pyx_n_s__idx; static PyObject *__pyx_n_s__isinf; static PyObject *__pyx_n_s__j; static PyObject *__pyx_n_s__low; static PyObject *__pyx_n_s__map_colors; static PyObject *__pyx_n_s__map_colors_uint8; static PyObject *__pyx_n_s__mapped_image; static PyObject *__pyx_n_s__mask; static PyObject *__pyx_n_s__norm_data; static PyObject *__pyx_n_s__norm_value; static PyObject *__pyx_n_s__np; static PyObject *__pyx_n_s__numpy; static PyObject *__pyx_n_s__range; static PyObject *__pyx_n_s__range_diff; static PyObject *__pyx_n_s__red; static PyObject *__pyx_n_s__red_lut; static PyObject *__pyx_n_s__reshape; static PyObject *__pyx_n_s__rgba; static PyObject *__pyx_n_s__shape; static PyObject *__pyx_n_s__size; static PyObject *__pyx_n_s__steps; static PyObject *__pyx_n_s__sys; static PyObject *__pyx_n_s__uint8; static PyObject *__pyx_n_s__view; static PyObject *__pyx_int_0; static PyObject *__pyx_int_4; static PyObject *__pyx_int_15; static PyObject *__pyx_int_256; static PyObject *__pyx_k_slice_2; static PyObject *__pyx_k_tuple_1; static PyObject *__pyx_k_tuple_3; static PyObject *__pyx_k_tuple_4; static PyObject *__pyx_k_tuple_6; static PyObject *__pyx_k_tuple_8; static PyObject *__pyx_k_tuple_10; static PyObject *__pyx_k_tuple_13; static PyObject *__pyx_k_tuple_14; static PyObject *__pyx_k_tuple_16; static PyObject *__pyx_k_tuple_17; static PyObject *__pyx_k_tuple_21; static PyObject *__pyx_k_tuple_23; static PyObject *__pyx_k_codeobj_18; static PyObject *__pyx_k_codeobj_22; static PyObject *__pyx_k_codeobj_24; /* "chaco/_cython_speedups.pyx":16 * * # Inline max and min functions used below * cdef inline float float_max(float a, float b): # <<<<<<<<<<<<<< * return a if a >= b else b * */ static CYTHON_INLINE float __pyx_f_5chaco_16_cython_speedups_float_max(float __pyx_v_a, float __pyx_v_b) { float __pyx_r; __Pyx_RefNannyDeclarations float __pyx_t_1; __Pyx_RefNannySetupContext("float_max", 0); /* "chaco/_cython_speedups.pyx":17 * # Inline max and min functions used below * cdef inline float float_max(float a, float b): * return a if a >= b else b # <<<<<<<<<<<<<< * * cdef inline float float_min(float a, float b): */ if ((__pyx_v_a >= __pyx_v_b)) { __pyx_t_1 = __pyx_v_a; } else { __pyx_t_1 = __pyx_v_b; } __pyx_r = __pyx_t_1; goto __pyx_L0; __pyx_r = 0; __pyx_L0:; __Pyx_RefNannyFinishContext(); return __pyx_r; } /* "chaco/_cython_speedups.pyx":19 * return a if a >= b else b * * cdef inline float float_min(float a, float b): # <<<<<<<<<<<<<< * return a if a <= b else b * */ static CYTHON_INLINE float __pyx_f_5chaco_16_cython_speedups_float_min(float __pyx_v_a, float __pyx_v_b) { float __pyx_r; __Pyx_RefNannyDeclarations float __pyx_t_1; __Pyx_RefNannySetupContext("float_min", 0); /* "chaco/_cython_speedups.pyx":20 * * cdef inline float float_min(float a, float b): * return a if a <= b else b # <<<<<<<<<<<<<< * * @cython.wraparound(False) */ if ((__pyx_v_a <= __pyx_v_b)) { __pyx_t_1 = __pyx_v_a; } else { __pyx_t_1 = __pyx_v_b; } __pyx_r = __pyx_t_1; goto __pyx_L0; __pyx_r = 0; __pyx_L0:; __Pyx_RefNannyFinishContext(); return __pyx_r; } /* Python wrapper */ static PyObject *__pyx_pw_5chaco_16_cython_speedups_1map_colors(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/ static char __pyx_doc_5chaco_16_cython_speedups_map_colors[] = "Map colors from color lookup tables to a data array.\n\n This is used in ColorMapper.map_screen\n\n Parameters\n ----------\n data_array : ndarray\n The data array\n steps: int\n The number of steps in the color map (depth)\n low : float\n The low end of the data range\n high : float\n The high end of the data range\n red_lut : ndarray of float32\n The red channel lookup table\n green_lut : ndarray of float32\n The green channel lookup table\n blue_lut : ndarray of float32\n The blue channel lookup table\n alpha_lut : ndarray of float32\n The alpha channel lookup table\n \n Returns\n -------\n rgba: ndarray of float32\n The rgba values of data_array according to the lookup tables. The shape\n of this array is equal to data_array.shape + (4,).\n\n "; static PyMethodDef __pyx_mdef_5chaco_16_cython_speedups_1map_colors = {__Pyx_NAMESTR("map_colors"), (PyCFunction)__pyx_pw_5chaco_16_cython_speedups_1map_colors, METH_VARARGS|METH_KEYWORDS, __Pyx_DOCSTR(__pyx_doc_5chaco_16_cython_speedups_map_colors)}; static PyObject *__pyx_pw_5chaco_16_cython_speedups_1map_colors(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds) { PyObject *__pyx_v_data_array = 0; int __pyx_v_steps; float __pyx_v_low; float __pyx_v_high; PyArrayObject *__pyx_v_red_lut = 0; PyArrayObject *__pyx_v_green_lut = 0; PyArrayObject *__pyx_v_blue_lut = 0; PyArrayObject *__pyx_v_alpha_lut = 0; static PyObject **__pyx_pyargnames[] = {&__pyx_n_s__data_array,&__pyx_n_s__steps,&__pyx_n_s__low,&__pyx_n_s__high,&__pyx_n_s__red_lut,&__pyx_n_s__green_lut,&__pyx_n_s__blue_lut,&__pyx_n_s__alpha_lut,0}; PyObject *__pyx_r = 0; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("map_colors (wrapper)", 0); __pyx_self = __pyx_self; { PyObject* values[8] = {0,0,0,0,0,0,0,0}; if (unlikely(__pyx_kwds)) { Py_ssize_t kw_args; const Py_ssize_t pos_args = PyTuple_GET_SIZE(__pyx_args); switch (pos_args) { case 8: values[7] = PyTuple_GET_ITEM(__pyx_args, 7); case 7: values[6] = PyTuple_GET_ITEM(__pyx_args, 6); case 6: values[5] = PyTuple_GET_ITEM(__pyx_args, 5); case 5: values[4] = PyTuple_GET_ITEM(__pyx_args, 4); case 4: values[3] = PyTuple_GET_ITEM(__pyx_args, 3); case 3: values[2] = PyTuple_GET_ITEM(__pyx_args, 2); case 2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1); case 1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0); case 0: break; default: goto __pyx_L5_argtuple_error; } kw_args = PyDict_Size(__pyx_kwds); switch (pos_args) { case 0: values[0] = PyDict_GetItem(__pyx_kwds, __pyx_n_s__data_array); if (likely(values[0])) kw_args--; else goto __pyx_L5_argtuple_error; case 1: values[1] = PyDict_GetItem(__pyx_kwds, __pyx_n_s__steps); if (likely(values[1])) kw_args--; else { __Pyx_RaiseArgtupleInvalid("map_colors", 1, 8, 8, 1); {__pyx_filename = __pyx_f[0]; __pyx_lineno = 25; __pyx_clineno = __LINE__; goto __pyx_L3_error;} } case 2: values[2] = PyDict_GetItem(__pyx_kwds, __pyx_n_s__low); if (likely(values[2])) kw_args--; else { __Pyx_RaiseArgtupleInvalid("map_colors", 1, 8, 8, 2); {__pyx_filename = __pyx_f[0]; __pyx_lineno = 25; __pyx_clineno = __LINE__; goto __pyx_L3_error;} } case 3: values[3] = PyDict_GetItem(__pyx_kwds, __pyx_n_s__high); if (likely(values[3])) kw_args--; else { __Pyx_RaiseArgtupleInvalid("map_colors", 1, 8, 8, 3); {__pyx_filename = __pyx_f[0]; __pyx_lineno = 25; __pyx_clineno = __LINE__; goto __pyx_L3_error;} } case 4: values[4] = PyDict_GetItem(__pyx_kwds, __pyx_n_s__red_lut); if (likely(values[4])) kw_args--; else { __Pyx_RaiseArgtupleInvalid("map_colors", 1, 8, 8, 4); {__pyx_filename = __pyx_f[0]; __pyx_lineno = 25; __pyx_clineno = __LINE__; goto __pyx_L3_error;} } case 5: values[5] = PyDict_GetItem(__pyx_kwds, __pyx_n_s__green_lut); if (likely(values[5])) kw_args--; else { __Pyx_RaiseArgtupleInvalid("map_colors", 1, 8, 8, 5); {__pyx_filename = __pyx_f[0]; __pyx_lineno = 25; __pyx_clineno = __LINE__; goto __pyx_L3_error;} } case 6: values[6] = PyDict_GetItem(__pyx_kwds, __pyx_n_s__blue_lut); if (likely(values[6])) kw_args--; else { __Pyx_RaiseArgtupleInvalid("map_colors", 1, 8, 8, 6); {__pyx_filename = __pyx_f[0]; __pyx_lineno = 25; __pyx_clineno = __LINE__; goto __pyx_L3_error;} } case 7: values[7] = PyDict_GetItem(__pyx_kwds, __pyx_n_s__alpha_lut); if (likely(values[7])) kw_args--; else { __Pyx_RaiseArgtupleInvalid("map_colors", 1, 8, 8, 7); {__pyx_filename = __pyx_f[0]; __pyx_lineno = 25; __pyx_clineno = __LINE__; goto __pyx_L3_error;} } } if (unlikely(kw_args > 0)) { if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, pos_args, "map_colors") < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 25; __pyx_clineno = __LINE__; goto __pyx_L3_error;} } } else if (PyTuple_GET_SIZE(__pyx_args) != 8) { goto __pyx_L5_argtuple_error; } else { values[0] = PyTuple_GET_ITEM(__pyx_args, 0); values[1] = PyTuple_GET_ITEM(__pyx_args, 1); values[2] = PyTuple_GET_ITEM(__pyx_args, 2); values[3] = PyTuple_GET_ITEM(__pyx_args, 3); values[4] = PyTuple_GET_ITEM(__pyx_args, 4); values[5] = PyTuple_GET_ITEM(__pyx_args, 5); values[6] = PyTuple_GET_ITEM(__pyx_args, 6); values[7] = PyTuple_GET_ITEM(__pyx_args, 7); } __pyx_v_data_array = values[0]; __pyx_v_steps = __Pyx_PyInt_AsInt(values[1]); if (unlikely((__pyx_v_steps == (int)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 27; __pyx_clineno = __LINE__; goto __pyx_L3_error;} __pyx_v_low = __pyx_PyFloat_AsFloat(values[2]); if (unlikely((__pyx_v_low == (float)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 28; __pyx_clineno = __LINE__; goto __pyx_L3_error;} __pyx_v_high = __pyx_PyFloat_AsFloat(values[3]); if (unlikely((__pyx_v_high == (float)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 29; __pyx_clineno = __LINE__; goto __pyx_L3_error;} __pyx_v_red_lut = ((PyArrayObject *)values[4]); __pyx_v_green_lut = ((PyArrayObject *)values[5]); __pyx_v_blue_lut = ((PyArrayObject *)values[6]); __pyx_v_alpha_lut = ((PyArrayObject *)values[7]); } goto __pyx_L4_argument_unpacking_done; __pyx_L5_argtuple_error:; __Pyx_RaiseArgtupleInvalid("map_colors", 1, 8, 8, PyTuple_GET_SIZE(__pyx_args)); {__pyx_filename = __pyx_f[0]; __pyx_lineno = 25; __pyx_clineno = __LINE__; goto __pyx_L3_error;} __pyx_L3_error:; __Pyx_AddTraceback("chaco._cython_speedups.map_colors", __pyx_clineno, __pyx_lineno, __pyx_filename); __Pyx_RefNannyFinishContext(); return NULL; __pyx_L4_argument_unpacking_done:; if (unlikely(((PyObject *)__pyx_v_data_array) == Py_None)) { PyErr_Format(PyExc_TypeError, "Argument 'data_array' must not be None"); {__pyx_filename = __pyx_f[0]; __pyx_lineno = 26; __pyx_clineno = __LINE__; goto __pyx_L1_error;} } if (unlikely(!__Pyx_ArgTypeTest(((PyObject *)__pyx_v_red_lut), __pyx_ptype_5numpy_ndarray, 0, "red_lut", 0))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 30; __pyx_clineno = __LINE__; goto __pyx_L1_error;} if (unlikely(!__Pyx_ArgTypeTest(((PyObject *)__pyx_v_green_lut), __pyx_ptype_5numpy_ndarray, 0, "green_lut", 0))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 31; __pyx_clineno = __LINE__; goto __pyx_L1_error;} if (unlikely(!__Pyx_ArgTypeTest(((PyObject *)__pyx_v_blue_lut), __pyx_ptype_5numpy_ndarray, 0, "blue_lut", 0))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 32; __pyx_clineno = __LINE__; goto __pyx_L1_error;} if (unlikely(!__Pyx_ArgTypeTest(((PyObject *)__pyx_v_alpha_lut), __pyx_ptype_5numpy_ndarray, 0, "alpha_lut", 0))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 33; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __pyx_r = __pyx_pf_5chaco_16_cython_speedups_map_colors(__pyx_self, __pyx_v_data_array, __pyx_v_steps, __pyx_v_low, __pyx_v_high, __pyx_v_red_lut, __pyx_v_green_lut, __pyx_v_blue_lut, __pyx_v_alpha_lut); goto __pyx_L0; __pyx_L1_error:; __pyx_r = NULL; __pyx_L0:; __Pyx_RefNannyFinishContext(); return __pyx_r; } /* "chaco/_cython_speedups.pyx":25 * @cython.boundscheck(False) * @cython.cdivision(True) * def map_colors( # <<<<<<<<<<<<<< * data_array not None, * int steps, */ static PyObject *__pyx_pf_5chaco_16_cython_speedups_map_colors(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_data_array, int __pyx_v_steps, float __pyx_v_low, float __pyx_v_high, PyArrayObject *__pyx_v_red_lut, PyArrayObject *__pyx_v_green_lut, PyArrayObject *__pyx_v_blue_lut, PyArrayObject *__pyx_v_alpha_lut) { PyObject *__pyx_v_shape = NULL; int __pyx_v_i; int __pyx_v_idx; int __pyx_v_N; float __pyx_v_norm_value; float __pyx_v_range_diff; float __pyx_v_red; float __pyx_v_green; float __pyx_v_blue; float __pyx_v_alpha; PyArrayObject *__pyx_v_rgba = 0; PyArrayObject *__pyx_v_norm_data = 0; __Pyx_LocalBuf_ND __pyx_pybuffernd_alpha_lut; __Pyx_Buffer __pyx_pybuffer_alpha_lut; __Pyx_LocalBuf_ND __pyx_pybuffernd_blue_lut; __Pyx_Buffer __pyx_pybuffer_blue_lut; __Pyx_LocalBuf_ND __pyx_pybuffernd_green_lut; __Pyx_Buffer __pyx_pybuffer_green_lut; __Pyx_LocalBuf_ND __pyx_pybuffernd_norm_data; __Pyx_Buffer __pyx_pybuffer_norm_data; __Pyx_LocalBuf_ND __pyx_pybuffernd_red_lut; __Pyx_Buffer __pyx_pybuffer_red_lut; __Pyx_LocalBuf_ND __pyx_pybuffernd_rgba; __Pyx_Buffer __pyx_pybuffer_rgba; PyObject *__pyx_r = NULL; __Pyx_RefNannyDeclarations PyObject *__pyx_t_1 = NULL; PyObject *__pyx_t_2 = NULL; int __pyx_t_3; PyObject *__pyx_t_4 = NULL; PyObject *__pyx_t_5 = NULL; PyArrayObject *__pyx_t_6 = NULL; int __pyx_t_7; int __pyx_t_8; int __pyx_t_9; int __pyx_t_10; int __pyx_t_11; int __pyx_t_12; int __pyx_t_13; int __pyx_t_14; int __pyx_t_15; long __pyx_t_16; int __pyx_t_17; long __pyx_t_18; int __pyx_t_19; long __pyx_t_20; int __pyx_t_21; long __pyx_t_22; PyArrayObject *__pyx_t_23 = NULL; int __pyx_t_24; int __pyx_t_25; long __pyx_t_26; int __pyx_t_27; long __pyx_t_28; int __pyx_t_29; long __pyx_t_30; int __pyx_t_31; long __pyx_t_32; int __pyx_t_33; int __pyx_t_34; int __pyx_t_35; long __pyx_t_36; int __pyx_t_37; int __pyx_t_38; long __pyx_t_39; int __pyx_t_40; int __pyx_t_41; long __pyx_t_42; int __pyx_t_43; int __pyx_t_44; long __pyx_t_45; int __pyx_lineno = 0; const char *__pyx_filename = NULL; int __pyx_clineno = 0; __Pyx_RefNannySetupContext("map_colors", 0); __pyx_pybuffer_rgba.pybuffer.buf = NULL; __pyx_pybuffer_rgba.refcount = 0; __pyx_pybuffernd_rgba.data = NULL; __pyx_pybuffernd_rgba.rcbuffer = &__pyx_pybuffer_rgba; __pyx_pybuffer_norm_data.pybuffer.buf = NULL; __pyx_pybuffer_norm_data.refcount = 0; __pyx_pybuffernd_norm_data.data = NULL; __pyx_pybuffernd_norm_data.rcbuffer = &__pyx_pybuffer_norm_data; __pyx_pybuffer_red_lut.pybuffer.buf = NULL; __pyx_pybuffer_red_lut.refcount = 0; __pyx_pybuffernd_red_lut.data = NULL; __pyx_pybuffernd_red_lut.rcbuffer = &__pyx_pybuffer_red_lut; __pyx_pybuffer_green_lut.pybuffer.buf = NULL; __pyx_pybuffer_green_lut.refcount = 0; __pyx_pybuffernd_green_lut.data = NULL; __pyx_pybuffernd_green_lut.rcbuffer = &__pyx_pybuffer_green_lut; __pyx_pybuffer_blue_lut.pybuffer.buf = NULL; __pyx_pybuffer_blue_lut.refcount = 0; __pyx_pybuffernd_blue_lut.data = NULL; __pyx_pybuffernd_blue_lut.rcbuffer = &__pyx_pybuffer_blue_lut; __pyx_pybuffer_alpha_lut.pybuffer.buf = NULL; __pyx_pybuffer_alpha_lut.refcount = 0; __pyx_pybuffernd_alpha_lut.data = NULL; __pyx_pybuffernd_alpha_lut.rcbuffer = &__pyx_pybuffer_alpha_lut; { __Pyx_BufFmt_StackElem __pyx_stack[1]; if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_red_lut.rcbuffer->pybuffer, (PyObject*)__pyx_v_red_lut, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float32_t, PyBUF_FORMAT| PyBUF_STRIDES, 1, 0, __pyx_stack) == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 25; __pyx_clineno = __LINE__; goto __pyx_L1_error;} } __pyx_pybuffernd_red_lut.diminfo[0].strides = __pyx_pybuffernd_red_lut.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_red_lut.diminfo[0].shape = __pyx_pybuffernd_red_lut.rcbuffer->pybuffer.shape[0]; { __Pyx_BufFmt_StackElem __pyx_stack[1]; if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_green_lut.rcbuffer->pybuffer, (PyObject*)__pyx_v_green_lut, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float32_t, PyBUF_FORMAT| PyBUF_STRIDES, 1, 0, __pyx_stack) == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 25; __pyx_clineno = __LINE__; goto __pyx_L1_error;} } __pyx_pybuffernd_green_lut.diminfo[0].strides = __pyx_pybuffernd_green_lut.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_green_lut.diminfo[0].shape = __pyx_pybuffernd_green_lut.rcbuffer->pybuffer.shape[0]; { __Pyx_BufFmt_StackElem __pyx_stack[1]; if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_blue_lut.rcbuffer->pybuffer, (PyObject*)__pyx_v_blue_lut, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float32_t, PyBUF_FORMAT| PyBUF_STRIDES, 1, 0, __pyx_stack) == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 25; __pyx_clineno = __LINE__; goto __pyx_L1_error;} } __pyx_pybuffernd_blue_lut.diminfo[0].strides = __pyx_pybuffernd_blue_lut.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_blue_lut.diminfo[0].shape = __pyx_pybuffernd_blue_lut.rcbuffer->pybuffer.shape[0]; { __Pyx_BufFmt_StackElem __pyx_stack[1]; if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_alpha_lut.rcbuffer->pybuffer, (PyObject*)__pyx_v_alpha_lut, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float32_t, PyBUF_FORMAT| PyBUF_STRIDES, 1, 0, __pyx_stack) == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 25; __pyx_clineno = __LINE__; goto __pyx_L1_error;} } __pyx_pybuffernd_alpha_lut.diminfo[0].strides = __pyx_pybuffernd_alpha_lut.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_alpha_lut.diminfo[0].shape = __pyx_pybuffernd_alpha_lut.rcbuffer->pybuffer.shape[0]; /* "chaco/_cython_speedups.pyx":65 * * ''' * shape = data_array.shape + (4,) # <<<<<<<<<<<<<< * cdef int i, idx, N = data_array.size * cdef float norm_value, range_diff, red, green, blue, alpha */ __pyx_t_1 = PyObject_GetAttr(__pyx_v_data_array, __pyx_n_s__shape); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 65; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_1); __pyx_t_2 = PyNumber_Add(__pyx_t_1, ((PyObject *)__pyx_k_tuple_1)); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 65; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_2); __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; __pyx_v_shape = __pyx_t_2; __pyx_t_2 = 0; /* "chaco/_cython_speedups.pyx":66 * ''' * shape = data_array.shape + (4,) * cdef int i, idx, N = data_array.size # <<<<<<<<<<<<<< * cdef float norm_value, range_diff, red, green, blue, alpha * cdef np.ndarray[np.float32_t, ndim=2] rgba = np.empty((N,4), np.float32) */ __pyx_t_2 = PyObject_GetAttr(__pyx_v_data_array, __pyx_n_s__size); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 66; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_2); __pyx_t_3 = __Pyx_PyInt_AsInt(__pyx_t_2); if (unlikely((__pyx_t_3 == (int)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 66; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; __pyx_v_N = __pyx_t_3; /* "chaco/_cython_speedups.pyx":68 * cdef int i, idx, N = data_array.size * cdef float norm_value, range_diff, red, green, blue, alpha * cdef np.ndarray[np.float32_t, ndim=2] rgba = np.empty((N,4), np.float32) # <<<<<<<<<<<<<< * * range_diff = high - low */ __pyx_t_2 = __Pyx_GetName(__pyx_m, __pyx_n_s__np); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 68; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_2); __pyx_t_1 = PyObject_GetAttr(__pyx_t_2, __pyx_n_s__empty); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 68; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_1); __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; __pyx_t_2 = PyInt_FromLong(__pyx_v_N); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 68; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_2); __pyx_t_4 = PyTuple_New(2); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 68; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_4); PyTuple_SET_ITEM(__pyx_t_4, 0, __pyx_t_2); __Pyx_GIVEREF(__pyx_t_2); __Pyx_INCREF(__pyx_int_4); PyTuple_SET_ITEM(__pyx_t_4, 1, __pyx_int_4); __Pyx_GIVEREF(__pyx_int_4); __pyx_t_2 = 0; __pyx_t_2 = __Pyx_GetName(__pyx_m, __pyx_n_s__np); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 68; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_2); __pyx_t_5 = PyObject_GetAttr(__pyx_t_2, __pyx_n_s__float32); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 68; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_5); __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; __pyx_t_2 = PyTuple_New(2); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 68; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_2); PyTuple_SET_ITEM(__pyx_t_2, 0, ((PyObject *)__pyx_t_4)); __Pyx_GIVEREF(((PyObject *)__pyx_t_4)); PyTuple_SET_ITEM(__pyx_t_2, 1, __pyx_t_5); __Pyx_GIVEREF(__pyx_t_5); __pyx_t_4 = 0; __pyx_t_5 = 0; __pyx_t_5 = PyObject_Call(__pyx_t_1, ((PyObject *)__pyx_t_2), NULL); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 68; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_5); __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; __Pyx_DECREF(((PyObject *)__pyx_t_2)); __pyx_t_2 = 0; if (!(likely(((__pyx_t_5) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_5, __pyx_ptype_5numpy_ndarray))))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 68; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __pyx_t_6 = ((PyArrayObject *)__pyx_t_5); { __Pyx_BufFmt_StackElem __pyx_stack[1]; if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_rgba.rcbuffer->pybuffer, (PyObject*)__pyx_t_6, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float32_t, PyBUF_FORMAT| PyBUF_STRIDES| PyBUF_WRITABLE, 2, 0, __pyx_stack) == -1)) { __pyx_v_rgba = ((PyArrayObject *)Py_None); __Pyx_INCREF(Py_None); __pyx_pybuffernd_rgba.rcbuffer->pybuffer.buf = NULL; {__pyx_filename = __pyx_f[0]; __pyx_lineno = 68; __pyx_clineno = __LINE__; goto __pyx_L1_error;} } else {__pyx_pybuffernd_rgba.diminfo[0].strides = __pyx_pybuffernd_rgba.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_rgba.diminfo[0].shape = __pyx_pybuffernd_rgba.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_rgba.diminfo[1].strides = __pyx_pybuffernd_rgba.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_rgba.diminfo[1].shape = __pyx_pybuffernd_rgba.rcbuffer->pybuffer.shape[1]; } } __pyx_t_6 = 0; __pyx_v_rgba = ((PyArrayObject *)__pyx_t_5); __pyx_t_5 = 0; /* "chaco/_cython_speedups.pyx":70 * cdef np.ndarray[np.float32_t, ndim=2] rgba = np.empty((N,4), np.float32) * * range_diff = high - low # <<<<<<<<<<<<<< * * # Handle null range, or infinite range (which can happen during */ __pyx_v_range_diff = (__pyx_v_high - __pyx_v_low); /* "chaco/_cython_speedups.pyx":74 * # Handle null range, or infinite range (which can happen during * # initialization before range is connected to a data source). * if range_diff == 0.0 or np.isinf(range_diff): # <<<<<<<<<<<<<< * idx = (steps - 1) / 2 * # Pull these out here to avoid N lookups */ __pyx_t_7 = (__pyx_v_range_diff == 0.0); if (!__pyx_t_7) { __pyx_t_5 = __Pyx_GetName(__pyx_m, __pyx_n_s__np); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 74; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_5); __pyx_t_2 = PyObject_GetAttr(__pyx_t_5, __pyx_n_s__isinf); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 74; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_2); __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; __pyx_t_5 = PyFloat_FromDouble(__pyx_v_range_diff); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 74; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_5); __pyx_t_1 = PyTuple_New(1); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 74; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_1); PyTuple_SET_ITEM(__pyx_t_1, 0, __pyx_t_5); __Pyx_GIVEREF(__pyx_t_5); __pyx_t_5 = 0; __pyx_t_5 = PyObject_Call(__pyx_t_2, ((PyObject *)__pyx_t_1), NULL); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 74; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_5); __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; __Pyx_DECREF(((PyObject *)__pyx_t_1)); __pyx_t_1 = 0; __pyx_t_8 = __Pyx_PyObject_IsTrue(__pyx_t_5); if (unlikely(__pyx_t_8 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 74; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; __pyx_t_9 = __pyx_t_8; } else { __pyx_t_9 = __pyx_t_7; } if (__pyx_t_9) { /* "chaco/_cython_speedups.pyx":75 * # initialization before range is connected to a data source). * if range_diff == 0.0 or np.isinf(range_diff): * idx = (steps - 1) / 2 # <<<<<<<<<<<<<< * # Pull these out here to avoid N lookups * red = red_lut[idx] */ __pyx_v_idx = ((__pyx_v_steps - 1) / 2); /* "chaco/_cython_speedups.pyx":77 * idx = (steps - 1) / 2 * # Pull these out here to avoid N lookups * red = red_lut[idx] # <<<<<<<<<<<<<< * green = green_lut[idx] * blue = blue_lut[idx] */ __pyx_t_3 = __pyx_v_idx; __pyx_v_red = (*__Pyx_BufPtrStrided1d(__pyx_t_5numpy_float32_t *, __pyx_pybuffernd_red_lut.rcbuffer->pybuffer.buf, __pyx_t_3, __pyx_pybuffernd_red_lut.diminfo[0].strides)); /* "chaco/_cython_speedups.pyx":78 * # Pull these out here to avoid N lookups * red = red_lut[idx] * green = green_lut[idx] # <<<<<<<<<<<<<< * blue = blue_lut[idx] * alpha = alpha_lut[idx] */ __pyx_t_10 = __pyx_v_idx; __pyx_v_green = (*__Pyx_BufPtrStrided1d(__pyx_t_5numpy_float32_t *, __pyx_pybuffernd_green_lut.rcbuffer->pybuffer.buf, __pyx_t_10, __pyx_pybuffernd_green_lut.diminfo[0].strides)); /* "chaco/_cython_speedups.pyx":79 * red = red_lut[idx] * green = green_lut[idx] * blue = blue_lut[idx] # <<<<<<<<<<<<<< * alpha = alpha_lut[idx] * for i in range(N): */ __pyx_t_11 = __pyx_v_idx; __pyx_v_blue = (*__Pyx_BufPtrStrided1d(__pyx_t_5numpy_float32_t *, __pyx_pybuffernd_blue_lut.rcbuffer->pybuffer.buf, __pyx_t_11, __pyx_pybuffernd_blue_lut.diminfo[0].strides)); /* "chaco/_cython_speedups.pyx":80 * green = green_lut[idx] * blue = blue_lut[idx] * alpha = alpha_lut[idx] # <<<<<<<<<<<<<< * for i in range(N): * rgba[i,0] = red */ __pyx_t_12 = __pyx_v_idx; __pyx_v_alpha = (*__Pyx_BufPtrStrided1d(__pyx_t_5numpy_float32_t *, __pyx_pybuffernd_alpha_lut.rcbuffer->pybuffer.buf, __pyx_t_12, __pyx_pybuffernd_alpha_lut.diminfo[0].strides)); /* "chaco/_cython_speedups.pyx":81 * blue = blue_lut[idx] * alpha = alpha_lut[idx] * for i in range(N): # <<<<<<<<<<<<<< * rgba[i,0] = red * rgba[i,1] = green */ __pyx_t_13 = __pyx_v_N; for (__pyx_t_14 = 0; __pyx_t_14 < __pyx_t_13; __pyx_t_14+=1) { __pyx_v_i = __pyx_t_14; /* "chaco/_cython_speedups.pyx":82 * alpha = alpha_lut[idx] * for i in range(N): * rgba[i,0] = red # <<<<<<<<<<<<<< * rgba[i,1] = green * rgba[i,2] = blue */ __pyx_t_15 = __pyx_v_i; __pyx_t_16 = 0; *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_float32_t *, __pyx_pybuffernd_rgba.rcbuffer->pybuffer.buf, __pyx_t_15, __pyx_pybuffernd_rgba.diminfo[0].strides, __pyx_t_16, __pyx_pybuffernd_rgba.diminfo[1].strides) = __pyx_v_red; /* "chaco/_cython_speedups.pyx":83 * for i in range(N): * rgba[i,0] = red * rgba[i,1] = green # <<<<<<<<<<<<<< * rgba[i,2] = blue * rgba[i,3] = alpha */ __pyx_t_17 = __pyx_v_i; __pyx_t_18 = 1; *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_float32_t *, __pyx_pybuffernd_rgba.rcbuffer->pybuffer.buf, __pyx_t_17, __pyx_pybuffernd_rgba.diminfo[0].strides, __pyx_t_18, __pyx_pybuffernd_rgba.diminfo[1].strides) = __pyx_v_green; /* "chaco/_cython_speedups.pyx":84 * rgba[i,0] = red * rgba[i,1] = green * rgba[i,2] = blue # <<<<<<<<<<<<<< * rgba[i,3] = alpha * return rgba.reshape(shape) */ __pyx_t_19 = __pyx_v_i; __pyx_t_20 = 2; *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_float32_t *, __pyx_pybuffernd_rgba.rcbuffer->pybuffer.buf, __pyx_t_19, __pyx_pybuffernd_rgba.diminfo[0].strides, __pyx_t_20, __pyx_pybuffernd_rgba.diminfo[1].strides) = __pyx_v_blue; /* "chaco/_cython_speedups.pyx":85 * rgba[i,1] = green * rgba[i,2] = blue * rgba[i,3] = alpha # <<<<<<<<<<<<<< * return rgba.reshape(shape) * */ __pyx_t_21 = __pyx_v_i; __pyx_t_22 = 3; *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_float32_t *, __pyx_pybuffernd_rgba.rcbuffer->pybuffer.buf, __pyx_t_21, __pyx_pybuffernd_rgba.diminfo[0].strides, __pyx_t_22, __pyx_pybuffernd_rgba.diminfo[1].strides) = __pyx_v_alpha; } /* "chaco/_cython_speedups.pyx":86 * rgba[i,2] = blue * rgba[i,3] = alpha * return rgba.reshape(shape) # <<<<<<<<<<<<<< * * # Copy the data into a float32 array so we can use fast iteration. We use */ __Pyx_XDECREF(__pyx_r); __pyx_t_5 = PyObject_GetAttr(((PyObject *)__pyx_v_rgba), __pyx_n_s__reshape); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 86; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_5); __pyx_t_1 = PyTuple_New(1); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 86; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_1); __Pyx_INCREF(__pyx_v_shape); PyTuple_SET_ITEM(__pyx_t_1, 0, __pyx_v_shape); __Pyx_GIVEREF(__pyx_v_shape); __pyx_t_2 = PyObject_Call(__pyx_t_5, ((PyObject *)__pyx_t_1), NULL); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 86; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_2); __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; __Pyx_DECREF(((PyObject *)__pyx_t_1)); __pyx_t_1 = 0; __pyx_r = __pyx_t_2; __pyx_t_2 = 0; goto __pyx_L0; goto __pyx_L3; } __pyx_L3:; /* "chaco/_cython_speedups.pyx":90 * # Copy the data into a float32 array so we can use fast iteration. We use * # part of the rgba array for this to save memory. * cdef np.ndarray[np.float32_t] norm_data = rgba[:,0] # <<<<<<<<<<<<<< * norm_data[:] = data_array.flat * */ __pyx_t_2 = PyObject_GetItem(((PyObject *)__pyx_v_rgba), ((PyObject *)__pyx_k_tuple_3)); if (!__pyx_t_2) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 90; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_2); if (!(likely(((__pyx_t_2) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_2, __pyx_ptype_5numpy_ndarray))))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 90; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __pyx_t_23 = ((PyArrayObject *)__pyx_t_2); { __Pyx_BufFmt_StackElem __pyx_stack[1]; if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_norm_data.rcbuffer->pybuffer, (PyObject*)__pyx_t_23, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float32_t, PyBUF_FORMAT| PyBUF_STRIDES, 1, 0, __pyx_stack) == -1)) { __pyx_v_norm_data = ((PyArrayObject *)Py_None); __Pyx_INCREF(Py_None); __pyx_pybuffernd_norm_data.rcbuffer->pybuffer.buf = NULL; {__pyx_filename = __pyx_f[0]; __pyx_lineno = 90; __pyx_clineno = __LINE__; goto __pyx_L1_error;} } else {__pyx_pybuffernd_norm_data.diminfo[0].strides = __pyx_pybuffernd_norm_data.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_norm_data.diminfo[0].shape = __pyx_pybuffernd_norm_data.rcbuffer->pybuffer.shape[0]; } } __pyx_t_23 = 0; __pyx_v_norm_data = ((PyArrayObject *)__pyx_t_2); __pyx_t_2 = 0; /* "chaco/_cython_speedups.pyx":91 * # part of the rgba array for this to save memory. * cdef np.ndarray[np.float32_t] norm_data = rgba[:,0] * norm_data[:] = data_array.flat # <<<<<<<<<<<<<< * * for i in range(N): */ __pyx_t_2 = PyObject_GetAttr(__pyx_v_data_array, __pyx_n_s__flat); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 91; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_2); if (__Pyx_PySequence_SetSlice(((PyObject *)__pyx_v_norm_data), 0, PY_SSIZE_T_MAX, __pyx_t_2) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 91; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; /* "chaco/_cython_speedups.pyx":93 * norm_data[:] = data_array.flat * * for i in range(N): # <<<<<<<<<<<<<< * if isnan(norm_data[i]): * rgba[i,0] = 0 */ __pyx_t_13 = __pyx_v_N; for (__pyx_t_14 = 0; __pyx_t_14 < __pyx_t_13; __pyx_t_14+=1) { __pyx_v_i = __pyx_t_14; /* "chaco/_cython_speedups.pyx":94 * * for i in range(N): * if isnan(norm_data[i]): # <<<<<<<<<<<<<< * rgba[i,0] = 0 * rgba[i,1] = 0 */ __pyx_t_24 = __pyx_v_i; __pyx_t_25 = isnan((*__Pyx_BufPtrStrided1d(__pyx_t_5numpy_float32_t *, __pyx_pybuffernd_norm_data.rcbuffer->pybuffer.buf, __pyx_t_24, __pyx_pybuffernd_norm_data.diminfo[0].strides))); if (__pyx_t_25) { /* "chaco/_cython_speedups.pyx":95 * for i in range(N): * if isnan(norm_data[i]): * rgba[i,0] = 0 # <<<<<<<<<<<<<< * rgba[i,1] = 0 * rgba[i,2] = 0 */ __pyx_t_25 = __pyx_v_i; __pyx_t_26 = 0; *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_float32_t *, __pyx_pybuffernd_rgba.rcbuffer->pybuffer.buf, __pyx_t_25, __pyx_pybuffernd_rgba.diminfo[0].strides, __pyx_t_26, __pyx_pybuffernd_rgba.diminfo[1].strides) = 0.0; /* "chaco/_cython_speedups.pyx":96 * if isnan(norm_data[i]): * rgba[i,0] = 0 * rgba[i,1] = 0 # <<<<<<<<<<<<<< * rgba[i,2] = 0 * rgba[i,3] = 0 */ __pyx_t_27 = __pyx_v_i; __pyx_t_28 = 1; *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_float32_t *, __pyx_pybuffernd_rgba.rcbuffer->pybuffer.buf, __pyx_t_27, __pyx_pybuffernd_rgba.diminfo[0].strides, __pyx_t_28, __pyx_pybuffernd_rgba.diminfo[1].strides) = 0.0; /* "chaco/_cython_speedups.pyx":97 * rgba[i,0] = 0 * rgba[i,1] = 0 * rgba[i,2] = 0 # <<<<<<<<<<<<<< * rgba[i,3] = 0 * else: */ __pyx_t_29 = __pyx_v_i; __pyx_t_30 = 2; *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_float32_t *, __pyx_pybuffernd_rgba.rcbuffer->pybuffer.buf, __pyx_t_29, __pyx_pybuffernd_rgba.diminfo[0].strides, __pyx_t_30, __pyx_pybuffernd_rgba.diminfo[1].strides) = 0.0; /* "chaco/_cython_speedups.pyx":98 * rgba[i,1] = 0 * rgba[i,2] = 0 * rgba[i,3] = 0 # <<<<<<<<<<<<<< * else: * norm_value = (norm_data[i] - low) / range_diff */ __pyx_t_31 = __pyx_v_i; __pyx_t_32 = 3; *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_float32_t *, __pyx_pybuffernd_rgba.rcbuffer->pybuffer.buf, __pyx_t_31, __pyx_pybuffernd_rgba.diminfo[0].strides, __pyx_t_32, __pyx_pybuffernd_rgba.diminfo[1].strides) = 0.0; goto __pyx_L8; } /*else*/ { /* "chaco/_cython_speedups.pyx":100 * rgba[i,3] = 0 * else: * norm_value = (norm_data[i] - low) / range_diff # <<<<<<<<<<<<<< * if norm_value > 1: * idx = steps - 1 */ __pyx_t_33 = __pyx_v_i; __pyx_v_norm_value = (((*__Pyx_BufPtrStrided1d(__pyx_t_5numpy_float32_t *, __pyx_pybuffernd_norm_data.rcbuffer->pybuffer.buf, __pyx_t_33, __pyx_pybuffernd_norm_data.diminfo[0].strides)) - __pyx_v_low) / __pyx_v_range_diff); /* "chaco/_cython_speedups.pyx":101 * else: * norm_value = (norm_data[i] - low) / range_diff * if norm_value > 1: # <<<<<<<<<<<<<< * idx = steps - 1 * elif norm_value < 0: */ __pyx_t_9 = (__pyx_v_norm_value > 1.0); if (__pyx_t_9) { /* "chaco/_cython_speedups.pyx":102 * norm_value = (norm_data[i] - low) / range_diff * if norm_value > 1: * idx = steps - 1 # <<<<<<<<<<<<<< * elif norm_value < 0: * idx = 0 */ __pyx_v_idx = (__pyx_v_steps - 1); goto __pyx_L9; } /* "chaco/_cython_speedups.pyx":103 * if norm_value > 1: * idx = steps - 1 * elif norm_value < 0: # <<<<<<<<<<<<<< * idx = 0 * else: */ __pyx_t_9 = (__pyx_v_norm_value < 0.0); if (__pyx_t_9) { /* "chaco/_cython_speedups.pyx":104 * idx = steps - 1 * elif norm_value < 0: * idx = 0 # <<<<<<<<<<<<<< * else: * idx = ((steps - 1) * norm_value) */ __pyx_v_idx = 0; goto __pyx_L9; } /*else*/ { /* "chaco/_cython_speedups.pyx":106 * idx = 0 * else: * idx = ((steps - 1) * norm_value) # <<<<<<<<<<<<<< * * rgba[i,0] = red_lut[idx] */ __pyx_v_idx = ((int)((__pyx_v_steps - 1) * __pyx_v_norm_value)); } __pyx_L9:; /* "chaco/_cython_speedups.pyx":108 * idx = ((steps - 1) * norm_value) * * rgba[i,0] = red_lut[idx] # <<<<<<<<<<<<<< * rgba[i,1] = green_lut[idx] * rgba[i,2] = blue_lut[idx] */ __pyx_t_34 = __pyx_v_idx; __pyx_t_35 = __pyx_v_i; __pyx_t_36 = 0; *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_float32_t *, __pyx_pybuffernd_rgba.rcbuffer->pybuffer.buf, __pyx_t_35, __pyx_pybuffernd_rgba.diminfo[0].strides, __pyx_t_36, __pyx_pybuffernd_rgba.diminfo[1].strides) = (*__Pyx_BufPtrStrided1d(__pyx_t_5numpy_float32_t *, __pyx_pybuffernd_red_lut.rcbuffer->pybuffer.buf, __pyx_t_34, __pyx_pybuffernd_red_lut.diminfo[0].strides)); /* "chaco/_cython_speedups.pyx":109 * * rgba[i,0] = red_lut[idx] * rgba[i,1] = green_lut[idx] # <<<<<<<<<<<<<< * rgba[i,2] = blue_lut[idx] * rgba[i,3] = alpha_lut[idx] */ __pyx_t_37 = __pyx_v_idx; __pyx_t_38 = __pyx_v_i; __pyx_t_39 = 1; *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_float32_t *, __pyx_pybuffernd_rgba.rcbuffer->pybuffer.buf, __pyx_t_38, __pyx_pybuffernd_rgba.diminfo[0].strides, __pyx_t_39, __pyx_pybuffernd_rgba.diminfo[1].strides) = (*__Pyx_BufPtrStrided1d(__pyx_t_5numpy_float32_t *, __pyx_pybuffernd_green_lut.rcbuffer->pybuffer.buf, __pyx_t_37, __pyx_pybuffernd_green_lut.diminfo[0].strides)); /* "chaco/_cython_speedups.pyx":110 * rgba[i,0] = red_lut[idx] * rgba[i,1] = green_lut[idx] * rgba[i,2] = blue_lut[idx] # <<<<<<<<<<<<<< * rgba[i,3] = alpha_lut[idx] * */ __pyx_t_40 = __pyx_v_idx; __pyx_t_41 = __pyx_v_i; __pyx_t_42 = 2; *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_float32_t *, __pyx_pybuffernd_rgba.rcbuffer->pybuffer.buf, __pyx_t_41, __pyx_pybuffernd_rgba.diminfo[0].strides, __pyx_t_42, __pyx_pybuffernd_rgba.diminfo[1].strides) = (*__Pyx_BufPtrStrided1d(__pyx_t_5numpy_float32_t *, __pyx_pybuffernd_blue_lut.rcbuffer->pybuffer.buf, __pyx_t_40, __pyx_pybuffernd_blue_lut.diminfo[0].strides)); /* "chaco/_cython_speedups.pyx":111 * rgba[i,1] = green_lut[idx] * rgba[i,2] = blue_lut[idx] * rgba[i,3] = alpha_lut[idx] # <<<<<<<<<<<<<< * * return rgba.reshape(shape) */ __pyx_t_43 = __pyx_v_idx; __pyx_t_44 = __pyx_v_i; __pyx_t_45 = 3; *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_float32_t *, __pyx_pybuffernd_rgba.rcbuffer->pybuffer.buf, __pyx_t_44, __pyx_pybuffernd_rgba.diminfo[0].strides, __pyx_t_45, __pyx_pybuffernd_rgba.diminfo[1].strides) = (*__Pyx_BufPtrStrided1d(__pyx_t_5numpy_float32_t *, __pyx_pybuffernd_alpha_lut.rcbuffer->pybuffer.buf, __pyx_t_43, __pyx_pybuffernd_alpha_lut.diminfo[0].strides)); } __pyx_L8:; } /* "chaco/_cython_speedups.pyx":113 * rgba[i,3] = alpha_lut[idx] * * return rgba.reshape(shape) # <<<<<<<<<<<<<< * * @cython.wraparound(False) */ __Pyx_XDECREF(__pyx_r); __pyx_t_2 = PyObject_GetAttr(((PyObject *)__pyx_v_rgba), __pyx_n_s__reshape); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 113; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_2); __pyx_t_1 = PyTuple_New(1); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 113; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_1); __Pyx_INCREF(__pyx_v_shape); PyTuple_SET_ITEM(__pyx_t_1, 0, __pyx_v_shape); __Pyx_GIVEREF(__pyx_v_shape); __pyx_t_5 = PyObject_Call(__pyx_t_2, ((PyObject *)__pyx_t_1), NULL); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 113; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_5); __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; __Pyx_DECREF(((PyObject *)__pyx_t_1)); __pyx_t_1 = 0; __pyx_r = __pyx_t_5; __pyx_t_5 = 0; goto __pyx_L0; __pyx_r = Py_None; __Pyx_INCREF(Py_None); goto __pyx_L0; __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_1); __Pyx_XDECREF(__pyx_t_2); __Pyx_XDECREF(__pyx_t_4); __Pyx_XDECREF(__pyx_t_5); { PyObject *__pyx_type, *__pyx_value, *__pyx_tb; __Pyx_ErrFetch(&__pyx_type, &__pyx_value, &__pyx_tb); __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_alpha_lut.rcbuffer->pybuffer); __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_blue_lut.rcbuffer->pybuffer); __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_green_lut.rcbuffer->pybuffer); __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_norm_data.rcbuffer->pybuffer); __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_red_lut.rcbuffer->pybuffer); __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_rgba.rcbuffer->pybuffer); __Pyx_ErrRestore(__pyx_type, __pyx_value, __pyx_tb);} __Pyx_AddTraceback("chaco._cython_speedups.map_colors", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_r = NULL; goto __pyx_L2; __pyx_L0:; __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_alpha_lut.rcbuffer->pybuffer); __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_blue_lut.rcbuffer->pybuffer); __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_green_lut.rcbuffer->pybuffer); __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_norm_data.rcbuffer->pybuffer); __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_red_lut.rcbuffer->pybuffer); __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_rgba.rcbuffer->pybuffer); __pyx_L2:; __Pyx_XDECREF(__pyx_v_shape); __Pyx_XDECREF((PyObject *)__pyx_v_rgba); __Pyx_XDECREF((PyObject *)__pyx_v_norm_data); __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); return __pyx_r; } /* Python wrapper */ static PyObject *__pyx_pw_5chaco_16_cython_speedups_3map_colors_uint8(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/ static char __pyx_doc_5chaco_16_cython_speedups_2map_colors_uint8[] = "\n Map colors from color lookup tables to a data array.\n\n Parameters\n ----------\n data_array : ndarray\n The data array\n steps: int\n The number of steps in the color map (depth)\n low : float\n The low end of the data range\n high : float\n The high end of the data range\n red_lut : ndarray of uint8\n The red channel lookup table\n green_lut : ndarray of uint8\n The green channel lookup table\n blue_lut : ndarray of uint8\n The blue channel lookup table\n alpha_lut : ndarray of uint8\n The alpha channel lookup table\n\n Returns\n -------\n rgba: ndarray of uint8\n The rgba values of data_array according to the lookup tables. The shape\n of this array is equal to data_array.shape + (4,).\n\n "; static PyMethodDef __pyx_mdef_5chaco_16_cython_speedups_3map_colors_uint8 = {__Pyx_NAMESTR("map_colors_uint8"), (PyCFunction)__pyx_pw_5chaco_16_cython_speedups_3map_colors_uint8, METH_VARARGS|METH_KEYWORDS, __Pyx_DOCSTR(__pyx_doc_5chaco_16_cython_speedups_2map_colors_uint8)}; static PyObject *__pyx_pw_5chaco_16_cython_speedups_3map_colors_uint8(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds) { PyObject *__pyx_v_data_array = 0; int __pyx_v_steps; float __pyx_v_low; float __pyx_v_high; PyArrayObject *__pyx_v_red_lut = 0; PyArrayObject *__pyx_v_green_lut = 0; PyArrayObject *__pyx_v_blue_lut = 0; PyArrayObject *__pyx_v_alpha_lut = 0; static PyObject **__pyx_pyargnames[] = {&__pyx_n_s__data_array,&__pyx_n_s__steps,&__pyx_n_s__low,&__pyx_n_s__high,&__pyx_n_s__red_lut,&__pyx_n_s__green_lut,&__pyx_n_s__blue_lut,&__pyx_n_s__alpha_lut,0}; PyObject *__pyx_r = 0; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("map_colors_uint8 (wrapper)", 0); __pyx_self = __pyx_self; { PyObject* values[8] = {0,0,0,0,0,0,0,0}; if (unlikely(__pyx_kwds)) { Py_ssize_t kw_args; const Py_ssize_t pos_args = PyTuple_GET_SIZE(__pyx_args); switch (pos_args) { case 8: values[7] = PyTuple_GET_ITEM(__pyx_args, 7); case 7: values[6] = PyTuple_GET_ITEM(__pyx_args, 6); case 6: values[5] = PyTuple_GET_ITEM(__pyx_args, 5); case 5: values[4] = PyTuple_GET_ITEM(__pyx_args, 4); case 4: values[3] = PyTuple_GET_ITEM(__pyx_args, 3); case 3: values[2] = PyTuple_GET_ITEM(__pyx_args, 2); case 2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1); case 1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0); case 0: break; default: goto __pyx_L5_argtuple_error; } kw_args = PyDict_Size(__pyx_kwds); switch (pos_args) { case 0: values[0] = PyDict_GetItem(__pyx_kwds, __pyx_n_s__data_array); if (likely(values[0])) kw_args--; else goto __pyx_L5_argtuple_error; case 1: values[1] = PyDict_GetItem(__pyx_kwds, __pyx_n_s__steps); if (likely(values[1])) kw_args--; else { __Pyx_RaiseArgtupleInvalid("map_colors_uint8", 1, 8, 8, 1); {__pyx_filename = __pyx_f[0]; __pyx_lineno = 118; __pyx_clineno = __LINE__; goto __pyx_L3_error;} } case 2: values[2] = PyDict_GetItem(__pyx_kwds, __pyx_n_s__low); if (likely(values[2])) kw_args--; else { __Pyx_RaiseArgtupleInvalid("map_colors_uint8", 1, 8, 8, 2); {__pyx_filename = __pyx_f[0]; __pyx_lineno = 118; __pyx_clineno = __LINE__; goto __pyx_L3_error;} } case 3: values[3] = PyDict_GetItem(__pyx_kwds, __pyx_n_s__high); if (likely(values[3])) kw_args--; else { __Pyx_RaiseArgtupleInvalid("map_colors_uint8", 1, 8, 8, 3); {__pyx_filename = __pyx_f[0]; __pyx_lineno = 118; __pyx_clineno = __LINE__; goto __pyx_L3_error;} } case 4: values[4] = PyDict_GetItem(__pyx_kwds, __pyx_n_s__red_lut); if (likely(values[4])) kw_args--; else { __Pyx_RaiseArgtupleInvalid("map_colors_uint8", 1, 8, 8, 4); {__pyx_filename = __pyx_f[0]; __pyx_lineno = 118; __pyx_clineno = __LINE__; goto __pyx_L3_error;} } case 5: values[5] = PyDict_GetItem(__pyx_kwds, __pyx_n_s__green_lut); if (likely(values[5])) kw_args--; else { __Pyx_RaiseArgtupleInvalid("map_colors_uint8", 1, 8, 8, 5); {__pyx_filename = __pyx_f[0]; __pyx_lineno = 118; __pyx_clineno = __LINE__; goto __pyx_L3_error;} } case 6: values[6] = PyDict_GetItem(__pyx_kwds, __pyx_n_s__blue_lut); if (likely(values[6])) kw_args--; else { __Pyx_RaiseArgtupleInvalid("map_colors_uint8", 1, 8, 8, 6); {__pyx_filename = __pyx_f[0]; __pyx_lineno = 118; __pyx_clineno = __LINE__; goto __pyx_L3_error;} } case 7: values[7] = PyDict_GetItem(__pyx_kwds, __pyx_n_s__alpha_lut); if (likely(values[7])) kw_args--; else { __Pyx_RaiseArgtupleInvalid("map_colors_uint8", 1, 8, 8, 7); {__pyx_filename = __pyx_f[0]; __pyx_lineno = 118; __pyx_clineno = __LINE__; goto __pyx_L3_error;} } } if (unlikely(kw_args > 0)) { if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, pos_args, "map_colors_uint8") < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 118; __pyx_clineno = __LINE__; goto __pyx_L3_error;} } } else if (PyTuple_GET_SIZE(__pyx_args) != 8) { goto __pyx_L5_argtuple_error; } else { values[0] = PyTuple_GET_ITEM(__pyx_args, 0); values[1] = PyTuple_GET_ITEM(__pyx_args, 1); values[2] = PyTuple_GET_ITEM(__pyx_args, 2); values[3] = PyTuple_GET_ITEM(__pyx_args, 3); values[4] = PyTuple_GET_ITEM(__pyx_args, 4); values[5] = PyTuple_GET_ITEM(__pyx_args, 5); values[6] = PyTuple_GET_ITEM(__pyx_args, 6); values[7] = PyTuple_GET_ITEM(__pyx_args, 7); } __pyx_v_data_array = values[0]; __pyx_v_steps = __Pyx_PyInt_AsInt(values[1]); if (unlikely((__pyx_v_steps == (int)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 119; __pyx_clineno = __LINE__; goto __pyx_L3_error;} __pyx_v_low = __pyx_PyFloat_AsFloat(values[2]); if (unlikely((__pyx_v_low == (float)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 120; __pyx_clineno = __LINE__; goto __pyx_L3_error;} __pyx_v_high = __pyx_PyFloat_AsFloat(values[3]); if (unlikely((__pyx_v_high == (float)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 121; __pyx_clineno = __LINE__; goto __pyx_L3_error;} __pyx_v_red_lut = ((PyArrayObject *)values[4]); __pyx_v_green_lut = ((PyArrayObject *)values[5]); __pyx_v_blue_lut = ((PyArrayObject *)values[6]); __pyx_v_alpha_lut = ((PyArrayObject *)values[7]); } goto __pyx_L4_argument_unpacking_done; __pyx_L5_argtuple_error:; __Pyx_RaiseArgtupleInvalid("map_colors_uint8", 1, 8, 8, PyTuple_GET_SIZE(__pyx_args)); {__pyx_filename = __pyx_f[0]; __pyx_lineno = 118; __pyx_clineno = __LINE__; goto __pyx_L3_error;} __pyx_L3_error:; __Pyx_AddTraceback("chaco._cython_speedups.map_colors_uint8", __pyx_clineno, __pyx_lineno, __pyx_filename); __Pyx_RefNannyFinishContext(); return NULL; __pyx_L4_argument_unpacking_done:; if (unlikely(((PyObject *)__pyx_v_data_array) == Py_None)) { PyErr_Format(PyExc_TypeError, "Argument 'data_array' must not be None"); {__pyx_filename = __pyx_f[0]; __pyx_lineno = 118; __pyx_clineno = __LINE__; goto __pyx_L1_error;} } if (unlikely(!__Pyx_ArgTypeTest(((PyObject *)__pyx_v_red_lut), __pyx_ptype_5numpy_ndarray, 0, "red_lut", 0))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 122; __pyx_clineno = __LINE__; goto __pyx_L1_error;} if (unlikely(!__Pyx_ArgTypeTest(((PyObject *)__pyx_v_green_lut), __pyx_ptype_5numpy_ndarray, 0, "green_lut", 0))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 123; __pyx_clineno = __LINE__; goto __pyx_L1_error;} if (unlikely(!__Pyx_ArgTypeTest(((PyObject *)__pyx_v_blue_lut), __pyx_ptype_5numpy_ndarray, 0, "blue_lut", 0))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 124; __pyx_clineno = __LINE__; goto __pyx_L1_error;} if (unlikely(!__Pyx_ArgTypeTest(((PyObject *)__pyx_v_alpha_lut), __pyx_ptype_5numpy_ndarray, 0, "alpha_lut", 0))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 125; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __pyx_r = __pyx_pf_5chaco_16_cython_speedups_2map_colors_uint8(__pyx_self, __pyx_v_data_array, __pyx_v_steps, __pyx_v_low, __pyx_v_high, __pyx_v_red_lut, __pyx_v_green_lut, __pyx_v_blue_lut, __pyx_v_alpha_lut); goto __pyx_L0; __pyx_L1_error:; __pyx_r = NULL; __pyx_L0:; __Pyx_RefNannyFinishContext(); return __pyx_r; } /* "chaco/_cython_speedups.pyx":118 * @cython.boundscheck(False) * @cython.cdivision(True) * def map_colors_uint8(data_array not None, # <<<<<<<<<<<<<< * int steps, * float low, */ static PyObject *__pyx_pf_5chaco_16_cython_speedups_2map_colors_uint8(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_data_array, int __pyx_v_steps, float __pyx_v_low, float __pyx_v_high, PyArrayObject *__pyx_v_red_lut, PyArrayObject *__pyx_v_green_lut, PyArrayObject *__pyx_v_blue_lut, PyArrayObject *__pyx_v_alpha_lut) { PyObject *__pyx_v_shape = NULL; int __pyx_v_i; int __pyx_v_idx; int __pyx_v_N; float __pyx_v_norm_value; float __pyx_v_range_diff; char __pyx_v_red; char __pyx_v_green; char __pyx_v_blue; char __pyx_v_alpha; PyArrayObject *__pyx_v_rgba = 0; PyArrayObject *__pyx_v_norm_data = 0; __Pyx_LocalBuf_ND __pyx_pybuffernd_alpha_lut; __Pyx_Buffer __pyx_pybuffer_alpha_lut; __Pyx_LocalBuf_ND __pyx_pybuffernd_blue_lut; __Pyx_Buffer __pyx_pybuffer_blue_lut; __Pyx_LocalBuf_ND __pyx_pybuffernd_green_lut; __Pyx_Buffer __pyx_pybuffer_green_lut; __Pyx_LocalBuf_ND __pyx_pybuffernd_norm_data; __Pyx_Buffer __pyx_pybuffer_norm_data; __Pyx_LocalBuf_ND __pyx_pybuffernd_red_lut; __Pyx_Buffer __pyx_pybuffer_red_lut; __Pyx_LocalBuf_ND __pyx_pybuffernd_rgba; __Pyx_Buffer __pyx_pybuffer_rgba; PyObject *__pyx_r = NULL; __Pyx_RefNannyDeclarations PyObject *__pyx_t_1 = NULL; PyObject *__pyx_t_2 = NULL; int __pyx_t_3; PyObject *__pyx_t_4 = NULL; PyObject *__pyx_t_5 = NULL; PyArrayObject *__pyx_t_6 = NULL; int __pyx_t_7; int __pyx_t_8; int __pyx_t_9; int __pyx_t_10; int __pyx_t_11; int __pyx_t_12; int __pyx_t_13; int __pyx_t_14; int __pyx_t_15; long __pyx_t_16; int __pyx_t_17; long __pyx_t_18; int __pyx_t_19; long __pyx_t_20; int __pyx_t_21; long __pyx_t_22; PyArrayObject *__pyx_t_23 = NULL; int __pyx_t_24; int __pyx_t_25; long __pyx_t_26; int __pyx_t_27; long __pyx_t_28; int __pyx_t_29; long __pyx_t_30; int __pyx_t_31; long __pyx_t_32; int __pyx_t_33; int __pyx_t_34; int __pyx_t_35; long __pyx_t_36; int __pyx_t_37; int __pyx_t_38; long __pyx_t_39; int __pyx_t_40; int __pyx_t_41; long __pyx_t_42; int __pyx_t_43; int __pyx_t_44; long __pyx_t_45; int __pyx_lineno = 0; const char *__pyx_filename = NULL; int __pyx_clineno = 0; __Pyx_RefNannySetupContext("map_colors_uint8", 0); __pyx_pybuffer_rgba.pybuffer.buf = NULL; __pyx_pybuffer_rgba.refcount = 0; __pyx_pybuffernd_rgba.data = NULL; __pyx_pybuffernd_rgba.rcbuffer = &__pyx_pybuffer_rgba; __pyx_pybuffer_norm_data.pybuffer.buf = NULL; __pyx_pybuffer_norm_data.refcount = 0; __pyx_pybuffernd_norm_data.data = NULL; __pyx_pybuffernd_norm_data.rcbuffer = &__pyx_pybuffer_norm_data; __pyx_pybuffer_red_lut.pybuffer.buf = NULL; __pyx_pybuffer_red_lut.refcount = 0; __pyx_pybuffernd_red_lut.data = NULL; __pyx_pybuffernd_red_lut.rcbuffer = &__pyx_pybuffer_red_lut; __pyx_pybuffer_green_lut.pybuffer.buf = NULL; __pyx_pybuffer_green_lut.refcount = 0; __pyx_pybuffernd_green_lut.data = NULL; __pyx_pybuffernd_green_lut.rcbuffer = &__pyx_pybuffer_green_lut; __pyx_pybuffer_blue_lut.pybuffer.buf = NULL; __pyx_pybuffer_blue_lut.refcount = 0; __pyx_pybuffernd_blue_lut.data = NULL; __pyx_pybuffernd_blue_lut.rcbuffer = &__pyx_pybuffer_blue_lut; __pyx_pybuffer_alpha_lut.pybuffer.buf = NULL; __pyx_pybuffer_alpha_lut.refcount = 0; __pyx_pybuffernd_alpha_lut.data = NULL; __pyx_pybuffernd_alpha_lut.rcbuffer = &__pyx_pybuffer_alpha_lut; { __Pyx_BufFmt_StackElem __pyx_stack[1]; if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_red_lut.rcbuffer->pybuffer, (PyObject*)__pyx_v_red_lut, &__Pyx_TypeInfo_nn___pyx_t_5numpy_uint8_t, PyBUF_FORMAT| PyBUF_STRIDES, 1, 0, __pyx_stack) == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 118; __pyx_clineno = __LINE__; goto __pyx_L1_error;} } __pyx_pybuffernd_red_lut.diminfo[0].strides = __pyx_pybuffernd_red_lut.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_red_lut.diminfo[0].shape = __pyx_pybuffernd_red_lut.rcbuffer->pybuffer.shape[0]; { __Pyx_BufFmt_StackElem __pyx_stack[1]; if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_green_lut.rcbuffer->pybuffer, (PyObject*)__pyx_v_green_lut, &__Pyx_TypeInfo_nn___pyx_t_5numpy_uint8_t, PyBUF_FORMAT| PyBUF_STRIDES, 1, 0, __pyx_stack) == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 118; __pyx_clineno = __LINE__; goto __pyx_L1_error;} } __pyx_pybuffernd_green_lut.diminfo[0].strides = __pyx_pybuffernd_green_lut.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_green_lut.diminfo[0].shape = __pyx_pybuffernd_green_lut.rcbuffer->pybuffer.shape[0]; { __Pyx_BufFmt_StackElem __pyx_stack[1]; if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_blue_lut.rcbuffer->pybuffer, (PyObject*)__pyx_v_blue_lut, &__Pyx_TypeInfo_nn___pyx_t_5numpy_uint8_t, PyBUF_FORMAT| PyBUF_STRIDES, 1, 0, __pyx_stack) == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 118; __pyx_clineno = __LINE__; goto __pyx_L1_error;} } __pyx_pybuffernd_blue_lut.diminfo[0].strides = __pyx_pybuffernd_blue_lut.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_blue_lut.diminfo[0].shape = __pyx_pybuffernd_blue_lut.rcbuffer->pybuffer.shape[0]; { __Pyx_BufFmt_StackElem __pyx_stack[1]; if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_alpha_lut.rcbuffer->pybuffer, (PyObject*)__pyx_v_alpha_lut, &__Pyx_TypeInfo_nn___pyx_t_5numpy_uint8_t, PyBUF_FORMAT| PyBUF_STRIDES, 1, 0, __pyx_stack) == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 118; __pyx_clineno = __LINE__; goto __pyx_L1_error;} } __pyx_pybuffernd_alpha_lut.diminfo[0].strides = __pyx_pybuffernd_alpha_lut.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_alpha_lut.diminfo[0].shape = __pyx_pybuffernd_alpha_lut.rcbuffer->pybuffer.shape[0]; /* "chaco/_cython_speedups.pyx":156 * * ''' * shape = data_array.shape + (4,) # <<<<<<<<<<<<<< * cdef int i, idx, N = data_array.size * cdef float norm_value, range_diff */ __pyx_t_1 = PyObject_GetAttr(__pyx_v_data_array, __pyx_n_s__shape); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 156; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_1); __pyx_t_2 = PyNumber_Add(__pyx_t_1, ((PyObject *)__pyx_k_tuple_4)); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 156; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_2); __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; __pyx_v_shape = __pyx_t_2; __pyx_t_2 = 0; /* "chaco/_cython_speedups.pyx":157 * ''' * shape = data_array.shape + (4,) * cdef int i, idx, N = data_array.size # <<<<<<<<<<<<<< * cdef float norm_value, range_diff * cdef char red, green, blue, alpha */ __pyx_t_2 = PyObject_GetAttr(__pyx_v_data_array, __pyx_n_s__size); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 157; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_2); __pyx_t_3 = __Pyx_PyInt_AsInt(__pyx_t_2); if (unlikely((__pyx_t_3 == (int)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 157; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; __pyx_v_N = __pyx_t_3; /* "chaco/_cython_speedups.pyx":160 * cdef float norm_value, range_diff * cdef char red, green, blue, alpha * cdef np.ndarray[np.uint8_t, ndim=2] rgba = np.empty((N, 4), np.uint8) # <<<<<<<<<<<<<< * * range_diff = high - low */ __pyx_t_2 = __Pyx_GetName(__pyx_m, __pyx_n_s__np); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 160; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_2); __pyx_t_1 = PyObject_GetAttr(__pyx_t_2, __pyx_n_s__empty); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 160; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_1); __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; __pyx_t_2 = PyInt_FromLong(__pyx_v_N); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 160; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_2); __pyx_t_4 = PyTuple_New(2); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 160; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_4); PyTuple_SET_ITEM(__pyx_t_4, 0, __pyx_t_2); __Pyx_GIVEREF(__pyx_t_2); __Pyx_INCREF(__pyx_int_4); PyTuple_SET_ITEM(__pyx_t_4, 1, __pyx_int_4); __Pyx_GIVEREF(__pyx_int_4); __pyx_t_2 = 0; __pyx_t_2 = __Pyx_GetName(__pyx_m, __pyx_n_s__np); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 160; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_2); __pyx_t_5 = PyObject_GetAttr(__pyx_t_2, __pyx_n_s__uint8); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 160; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_5); __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; __pyx_t_2 = PyTuple_New(2); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 160; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_2); PyTuple_SET_ITEM(__pyx_t_2, 0, ((PyObject *)__pyx_t_4)); __Pyx_GIVEREF(((PyObject *)__pyx_t_4)); PyTuple_SET_ITEM(__pyx_t_2, 1, __pyx_t_5); __Pyx_GIVEREF(__pyx_t_5); __pyx_t_4 = 0; __pyx_t_5 = 0; __pyx_t_5 = PyObject_Call(__pyx_t_1, ((PyObject *)__pyx_t_2), NULL); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 160; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_5); __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; __Pyx_DECREF(((PyObject *)__pyx_t_2)); __pyx_t_2 = 0; if (!(likely(((__pyx_t_5) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_5, __pyx_ptype_5numpy_ndarray))))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 160; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __pyx_t_6 = ((PyArrayObject *)__pyx_t_5); { __Pyx_BufFmt_StackElem __pyx_stack[1]; if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_rgba.rcbuffer->pybuffer, (PyObject*)__pyx_t_6, &__Pyx_TypeInfo_nn___pyx_t_5numpy_uint8_t, PyBUF_FORMAT| PyBUF_STRIDES| PyBUF_WRITABLE, 2, 0, __pyx_stack) == -1)) { __pyx_v_rgba = ((PyArrayObject *)Py_None); __Pyx_INCREF(Py_None); __pyx_pybuffernd_rgba.rcbuffer->pybuffer.buf = NULL; {__pyx_filename = __pyx_f[0]; __pyx_lineno = 160; __pyx_clineno = __LINE__; goto __pyx_L1_error;} } else {__pyx_pybuffernd_rgba.diminfo[0].strides = __pyx_pybuffernd_rgba.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_rgba.diminfo[0].shape = __pyx_pybuffernd_rgba.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_rgba.diminfo[1].strides = __pyx_pybuffernd_rgba.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_rgba.diminfo[1].shape = __pyx_pybuffernd_rgba.rcbuffer->pybuffer.shape[1]; } } __pyx_t_6 = 0; __pyx_v_rgba = ((PyArrayObject *)__pyx_t_5); __pyx_t_5 = 0; /* "chaco/_cython_speedups.pyx":162 * cdef np.ndarray[np.uint8_t, ndim=2] rgba = np.empty((N, 4), np.uint8) * * range_diff = high - low # <<<<<<<<<<<<<< * * # Handle null range, or infinite range (which can happen during */ __pyx_v_range_diff = (__pyx_v_high - __pyx_v_low); /* "chaco/_cython_speedups.pyx":166 * # Handle null range, or infinite range (which can happen during * # initialization before range is connected to a data source). * if range_diff == 0.0 or np.isinf(range_diff): # <<<<<<<<<<<<<< * idx = (steps - 1) / 2 * # Pull these out here to avoid N lookups */ __pyx_t_7 = (__pyx_v_range_diff == 0.0); if (!__pyx_t_7) { __pyx_t_5 = __Pyx_GetName(__pyx_m, __pyx_n_s__np); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 166; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_5); __pyx_t_2 = PyObject_GetAttr(__pyx_t_5, __pyx_n_s__isinf); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 166; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_2); __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; __pyx_t_5 = PyFloat_FromDouble(__pyx_v_range_diff); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 166; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_5); __pyx_t_1 = PyTuple_New(1); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 166; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_1); PyTuple_SET_ITEM(__pyx_t_1, 0, __pyx_t_5); __Pyx_GIVEREF(__pyx_t_5); __pyx_t_5 = 0; __pyx_t_5 = PyObject_Call(__pyx_t_2, ((PyObject *)__pyx_t_1), NULL); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 166; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_5); __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; __Pyx_DECREF(((PyObject *)__pyx_t_1)); __pyx_t_1 = 0; __pyx_t_8 = __Pyx_PyObject_IsTrue(__pyx_t_5); if (unlikely(__pyx_t_8 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 166; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; __pyx_t_9 = __pyx_t_8; } else { __pyx_t_9 = __pyx_t_7; } if (__pyx_t_9) { /* "chaco/_cython_speedups.pyx":167 * # initialization before range is connected to a data source). * if range_diff == 0.0 or np.isinf(range_diff): * idx = (steps - 1) / 2 # <<<<<<<<<<<<<< * # Pull these out here to avoid N lookups * red = red_lut[idx] */ __pyx_v_idx = ((__pyx_v_steps - 1) / 2); /* "chaco/_cython_speedups.pyx":169 * idx = (steps - 1) / 2 * # Pull these out here to avoid N lookups * red = red_lut[idx] # <<<<<<<<<<<<<< * green = green_lut[idx] * blue = blue_lut[idx] */ __pyx_t_3 = __pyx_v_idx; __pyx_v_red = (*__Pyx_BufPtrStrided1d(__pyx_t_5numpy_uint8_t *, __pyx_pybuffernd_red_lut.rcbuffer->pybuffer.buf, __pyx_t_3, __pyx_pybuffernd_red_lut.diminfo[0].strides)); /* "chaco/_cython_speedups.pyx":170 * # Pull these out here to avoid N lookups * red = red_lut[idx] * green = green_lut[idx] # <<<<<<<<<<<<<< * blue = blue_lut[idx] * alpha = alpha_lut[idx] */ __pyx_t_10 = __pyx_v_idx; __pyx_v_green = (*__Pyx_BufPtrStrided1d(__pyx_t_5numpy_uint8_t *, __pyx_pybuffernd_green_lut.rcbuffer->pybuffer.buf, __pyx_t_10, __pyx_pybuffernd_green_lut.diminfo[0].strides)); /* "chaco/_cython_speedups.pyx":171 * red = red_lut[idx] * green = green_lut[idx] * blue = blue_lut[idx] # <<<<<<<<<<<<<< * alpha = alpha_lut[idx] * for i in range(N): */ __pyx_t_11 = __pyx_v_idx; __pyx_v_blue = (*__Pyx_BufPtrStrided1d(__pyx_t_5numpy_uint8_t *, __pyx_pybuffernd_blue_lut.rcbuffer->pybuffer.buf, __pyx_t_11, __pyx_pybuffernd_blue_lut.diminfo[0].strides)); /* "chaco/_cython_speedups.pyx":172 * green = green_lut[idx] * blue = blue_lut[idx] * alpha = alpha_lut[idx] # <<<<<<<<<<<<<< * for i in range(N): * rgba[i, 0] = red */ __pyx_t_12 = __pyx_v_idx; __pyx_v_alpha = (*__Pyx_BufPtrStrided1d(__pyx_t_5numpy_uint8_t *, __pyx_pybuffernd_alpha_lut.rcbuffer->pybuffer.buf, __pyx_t_12, __pyx_pybuffernd_alpha_lut.diminfo[0].strides)); /* "chaco/_cython_speedups.pyx":173 * blue = blue_lut[idx] * alpha = alpha_lut[idx] * for i in range(N): # <<<<<<<<<<<<<< * rgba[i, 0] = red * rgba[i, 1] = green */ __pyx_t_13 = __pyx_v_N; for (__pyx_t_14 = 0; __pyx_t_14 < __pyx_t_13; __pyx_t_14+=1) { __pyx_v_i = __pyx_t_14; /* "chaco/_cython_speedups.pyx":174 * alpha = alpha_lut[idx] * for i in range(N): * rgba[i, 0] = red # <<<<<<<<<<<<<< * rgba[i, 1] = green * rgba[i, 2] = blue */ __pyx_t_15 = __pyx_v_i; __pyx_t_16 = 0; *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_uint8_t *, __pyx_pybuffernd_rgba.rcbuffer->pybuffer.buf, __pyx_t_15, __pyx_pybuffernd_rgba.diminfo[0].strides, __pyx_t_16, __pyx_pybuffernd_rgba.diminfo[1].strides) = __pyx_v_red; /* "chaco/_cython_speedups.pyx":175 * for i in range(N): * rgba[i, 0] = red * rgba[i, 1] = green # <<<<<<<<<<<<<< * rgba[i, 2] = blue * rgba[i, 3] = alpha */ __pyx_t_17 = __pyx_v_i; __pyx_t_18 = 1; *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_uint8_t *, __pyx_pybuffernd_rgba.rcbuffer->pybuffer.buf, __pyx_t_17, __pyx_pybuffernd_rgba.diminfo[0].strides, __pyx_t_18, __pyx_pybuffernd_rgba.diminfo[1].strides) = __pyx_v_green; /* "chaco/_cython_speedups.pyx":176 * rgba[i, 0] = red * rgba[i, 1] = green * rgba[i, 2] = blue # <<<<<<<<<<<<<< * rgba[i, 3] = alpha * return rgba.reshape(shape) */ __pyx_t_19 = __pyx_v_i; __pyx_t_20 = 2; *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_uint8_t *, __pyx_pybuffernd_rgba.rcbuffer->pybuffer.buf, __pyx_t_19, __pyx_pybuffernd_rgba.diminfo[0].strides, __pyx_t_20, __pyx_pybuffernd_rgba.diminfo[1].strides) = __pyx_v_blue; /* "chaco/_cython_speedups.pyx":177 * rgba[i, 1] = green * rgba[i, 2] = blue * rgba[i, 3] = alpha # <<<<<<<<<<<<<< * return rgba.reshape(shape) * */ __pyx_t_21 = __pyx_v_i; __pyx_t_22 = 3; *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_uint8_t *, __pyx_pybuffernd_rgba.rcbuffer->pybuffer.buf, __pyx_t_21, __pyx_pybuffernd_rgba.diminfo[0].strides, __pyx_t_22, __pyx_pybuffernd_rgba.diminfo[1].strides) = __pyx_v_alpha; } /* "chaco/_cython_speedups.pyx":178 * rgba[i, 2] = blue * rgba[i, 3] = alpha * return rgba.reshape(shape) # <<<<<<<<<<<<<< * * # Copy the data into a float32 array so we can use fast iteration. */ __Pyx_XDECREF(__pyx_r); __pyx_t_5 = PyObject_GetAttr(((PyObject *)__pyx_v_rgba), __pyx_n_s__reshape); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 178; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_5); __pyx_t_1 = PyTuple_New(1); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 178; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_1); __Pyx_INCREF(__pyx_v_shape); PyTuple_SET_ITEM(__pyx_t_1, 0, __pyx_v_shape); __Pyx_GIVEREF(__pyx_v_shape); __pyx_t_2 = PyObject_Call(__pyx_t_5, ((PyObject *)__pyx_t_1), NULL); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 178; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_2); __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; __Pyx_DECREF(((PyObject *)__pyx_t_1)); __pyx_t_1 = 0; __pyx_r = __pyx_t_2; __pyx_t_2 = 0; goto __pyx_L0; goto __pyx_L3; } __pyx_L3:; /* "chaco/_cython_speedups.pyx":181 * * # Copy the data into a float32 array so we can use fast iteration. * cdef np.ndarray[np.float32_t] norm_data = np.empty(N, np.float32) # <<<<<<<<<<<<<< * norm_data[:] = data_array.flat * */ __pyx_t_2 = __Pyx_GetName(__pyx_m, __pyx_n_s__np); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 181; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_2); __pyx_t_1 = PyObject_GetAttr(__pyx_t_2, __pyx_n_s__empty); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 181; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_1); __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; __pyx_t_2 = PyInt_FromLong(__pyx_v_N); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 181; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_2); __pyx_t_5 = __Pyx_GetName(__pyx_m, __pyx_n_s__np); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 181; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_5); __pyx_t_4 = PyObject_GetAttr(__pyx_t_5, __pyx_n_s__float32); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 181; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_4); __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; __pyx_t_5 = PyTuple_New(2); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 181; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_5); PyTuple_SET_ITEM(__pyx_t_5, 0, __pyx_t_2); __Pyx_GIVEREF(__pyx_t_2); PyTuple_SET_ITEM(__pyx_t_5, 1, __pyx_t_4); __Pyx_GIVEREF(__pyx_t_4); __pyx_t_2 = 0; __pyx_t_4 = 0; __pyx_t_4 = PyObject_Call(__pyx_t_1, ((PyObject *)__pyx_t_5), NULL); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 181; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_4); __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; __Pyx_DECREF(((PyObject *)__pyx_t_5)); __pyx_t_5 = 0; if (!(likely(((__pyx_t_4) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_4, __pyx_ptype_5numpy_ndarray))))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 181; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __pyx_t_23 = ((PyArrayObject *)__pyx_t_4); { __Pyx_BufFmt_StackElem __pyx_stack[1]; if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_norm_data.rcbuffer->pybuffer, (PyObject*)__pyx_t_23, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float32_t, PyBUF_FORMAT| PyBUF_STRIDES, 1, 0, __pyx_stack) == -1)) { __pyx_v_norm_data = ((PyArrayObject *)Py_None); __Pyx_INCREF(Py_None); __pyx_pybuffernd_norm_data.rcbuffer->pybuffer.buf = NULL; {__pyx_filename = __pyx_f[0]; __pyx_lineno = 181; __pyx_clineno = __LINE__; goto __pyx_L1_error;} } else {__pyx_pybuffernd_norm_data.diminfo[0].strides = __pyx_pybuffernd_norm_data.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_norm_data.diminfo[0].shape = __pyx_pybuffernd_norm_data.rcbuffer->pybuffer.shape[0]; } } __pyx_t_23 = 0; __pyx_v_norm_data = ((PyArrayObject *)__pyx_t_4); __pyx_t_4 = 0; /* "chaco/_cython_speedups.pyx":182 * # Copy the data into a float32 array so we can use fast iteration. * cdef np.ndarray[np.float32_t] norm_data = np.empty(N, np.float32) * norm_data[:] = data_array.flat # <<<<<<<<<<<<<< * * for i in range(N): */ __pyx_t_4 = PyObject_GetAttr(__pyx_v_data_array, __pyx_n_s__flat); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 182; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_4); if (__Pyx_PySequence_SetSlice(((PyObject *)__pyx_v_norm_data), 0, PY_SSIZE_T_MAX, __pyx_t_4) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 182; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; /* "chaco/_cython_speedups.pyx":184 * norm_data[:] = data_array.flat * * for i in range(N): # <<<<<<<<<<<<<< * if isnan(norm_data[i]): * rgba[i, 0] = 0 */ __pyx_t_13 = __pyx_v_N; for (__pyx_t_14 = 0; __pyx_t_14 < __pyx_t_13; __pyx_t_14+=1) { __pyx_v_i = __pyx_t_14; /* "chaco/_cython_speedups.pyx":185 * * for i in range(N): * if isnan(norm_data[i]): # <<<<<<<<<<<<<< * rgba[i, 0] = 0 * rgba[i, 1] = 0 */ __pyx_t_24 = __pyx_v_i; __pyx_t_25 = isnan((*__Pyx_BufPtrStrided1d(__pyx_t_5numpy_float32_t *, __pyx_pybuffernd_norm_data.rcbuffer->pybuffer.buf, __pyx_t_24, __pyx_pybuffernd_norm_data.diminfo[0].strides))); if (__pyx_t_25) { /* "chaco/_cython_speedups.pyx":186 * for i in range(N): * if isnan(norm_data[i]): * rgba[i, 0] = 0 # <<<<<<<<<<<<<< * rgba[i, 1] = 0 * rgba[i, 2] = 0 */ __pyx_t_25 = __pyx_v_i; __pyx_t_26 = 0; *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_uint8_t *, __pyx_pybuffernd_rgba.rcbuffer->pybuffer.buf, __pyx_t_25, __pyx_pybuffernd_rgba.diminfo[0].strides, __pyx_t_26, __pyx_pybuffernd_rgba.diminfo[1].strides) = 0; /* "chaco/_cython_speedups.pyx":187 * if isnan(norm_data[i]): * rgba[i, 0] = 0 * rgba[i, 1] = 0 # <<<<<<<<<<<<<< * rgba[i, 2] = 0 * rgba[i, 3] = 0 */ __pyx_t_27 = __pyx_v_i; __pyx_t_28 = 1; *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_uint8_t *, __pyx_pybuffernd_rgba.rcbuffer->pybuffer.buf, __pyx_t_27, __pyx_pybuffernd_rgba.diminfo[0].strides, __pyx_t_28, __pyx_pybuffernd_rgba.diminfo[1].strides) = 0; /* "chaco/_cython_speedups.pyx":188 * rgba[i, 0] = 0 * rgba[i, 1] = 0 * rgba[i, 2] = 0 # <<<<<<<<<<<<<< * rgba[i, 3] = 0 * else: */ __pyx_t_29 = __pyx_v_i; __pyx_t_30 = 2; *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_uint8_t *, __pyx_pybuffernd_rgba.rcbuffer->pybuffer.buf, __pyx_t_29, __pyx_pybuffernd_rgba.diminfo[0].strides, __pyx_t_30, __pyx_pybuffernd_rgba.diminfo[1].strides) = 0; /* "chaco/_cython_speedups.pyx":189 * rgba[i, 1] = 0 * rgba[i, 2] = 0 * rgba[i, 3] = 0 # <<<<<<<<<<<<<< * else: * # range_diff has already been confirmed as non-zero */ __pyx_t_31 = __pyx_v_i; __pyx_t_32 = 3; *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_uint8_t *, __pyx_pybuffernd_rgba.rcbuffer->pybuffer.buf, __pyx_t_31, __pyx_pybuffernd_rgba.diminfo[0].strides, __pyx_t_32, __pyx_pybuffernd_rgba.diminfo[1].strides) = 0; goto __pyx_L8; } /*else*/ { /* "chaco/_cython_speedups.pyx":192 * else: * # range_diff has already been confirmed as non-zero * norm_value = (norm_data[i] - low) / range_diff # <<<<<<<<<<<<<< * if norm_value > 1: * idx = steps - 1 */ __pyx_t_33 = __pyx_v_i; __pyx_v_norm_value = (((*__Pyx_BufPtrStrided1d(__pyx_t_5numpy_float32_t *, __pyx_pybuffernd_norm_data.rcbuffer->pybuffer.buf, __pyx_t_33, __pyx_pybuffernd_norm_data.diminfo[0].strides)) - __pyx_v_low) / __pyx_v_range_diff); /* "chaco/_cython_speedups.pyx":193 * # range_diff has already been confirmed as non-zero * norm_value = (norm_data[i] - low) / range_diff * if norm_value > 1: # <<<<<<<<<<<<<< * idx = steps - 1 * elif norm_value < 0: */ __pyx_t_9 = (__pyx_v_norm_value > 1.0); if (__pyx_t_9) { /* "chaco/_cython_speedups.pyx":194 * norm_value = (norm_data[i] - low) / range_diff * if norm_value > 1: * idx = steps - 1 # <<<<<<<<<<<<<< * elif norm_value < 0: * idx = 0 */ __pyx_v_idx = (__pyx_v_steps - 1); goto __pyx_L9; } /* "chaco/_cython_speedups.pyx":195 * if norm_value > 1: * idx = steps - 1 * elif norm_value < 0: # <<<<<<<<<<<<<< * idx = 0 * else: */ __pyx_t_9 = (__pyx_v_norm_value < 0.0); if (__pyx_t_9) { /* "chaco/_cython_speedups.pyx":196 * idx = steps - 1 * elif norm_value < 0: * idx = 0 # <<<<<<<<<<<<<< * else: * idx = ((steps - 1) * norm_value) */ __pyx_v_idx = 0; goto __pyx_L9; } /*else*/ { /* "chaco/_cython_speedups.pyx":198 * idx = 0 * else: * idx = ((steps - 1) * norm_value) # <<<<<<<<<<<<<< * * rgba[i, 0] = red_lut[idx] */ __pyx_v_idx = ((int)((__pyx_v_steps - 1) * __pyx_v_norm_value)); } __pyx_L9:; /* "chaco/_cython_speedups.pyx":200 * idx = ((steps - 1) * norm_value) * * rgba[i, 0] = red_lut[idx] # <<<<<<<<<<<<<< * rgba[i, 1] = green_lut[idx] * rgba[i, 2] = blue_lut[idx] */ __pyx_t_34 = __pyx_v_idx; __pyx_t_35 = __pyx_v_i; __pyx_t_36 = 0; *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_uint8_t *, __pyx_pybuffernd_rgba.rcbuffer->pybuffer.buf, __pyx_t_35, __pyx_pybuffernd_rgba.diminfo[0].strides, __pyx_t_36, __pyx_pybuffernd_rgba.diminfo[1].strides) = (*__Pyx_BufPtrStrided1d(__pyx_t_5numpy_uint8_t *, __pyx_pybuffernd_red_lut.rcbuffer->pybuffer.buf, __pyx_t_34, __pyx_pybuffernd_red_lut.diminfo[0].strides)); /* "chaco/_cython_speedups.pyx":201 * * rgba[i, 0] = red_lut[idx] * rgba[i, 1] = green_lut[idx] # <<<<<<<<<<<<<< * rgba[i, 2] = blue_lut[idx] * rgba[i, 3] = alpha_lut[idx] */ __pyx_t_37 = __pyx_v_idx; __pyx_t_38 = __pyx_v_i; __pyx_t_39 = 1; *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_uint8_t *, __pyx_pybuffernd_rgba.rcbuffer->pybuffer.buf, __pyx_t_38, __pyx_pybuffernd_rgba.diminfo[0].strides, __pyx_t_39, __pyx_pybuffernd_rgba.diminfo[1].strides) = (*__Pyx_BufPtrStrided1d(__pyx_t_5numpy_uint8_t *, __pyx_pybuffernd_green_lut.rcbuffer->pybuffer.buf, __pyx_t_37, __pyx_pybuffernd_green_lut.diminfo[0].strides)); /* "chaco/_cython_speedups.pyx":202 * rgba[i, 0] = red_lut[idx] * rgba[i, 1] = green_lut[idx] * rgba[i, 2] = blue_lut[idx] # <<<<<<<<<<<<<< * rgba[i, 3] = alpha_lut[idx] * */ __pyx_t_40 = __pyx_v_idx; __pyx_t_41 = __pyx_v_i; __pyx_t_42 = 2; *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_uint8_t *, __pyx_pybuffernd_rgba.rcbuffer->pybuffer.buf, __pyx_t_41, __pyx_pybuffernd_rgba.diminfo[0].strides, __pyx_t_42, __pyx_pybuffernd_rgba.diminfo[1].strides) = (*__Pyx_BufPtrStrided1d(__pyx_t_5numpy_uint8_t *, __pyx_pybuffernd_blue_lut.rcbuffer->pybuffer.buf, __pyx_t_40, __pyx_pybuffernd_blue_lut.diminfo[0].strides)); /* "chaco/_cython_speedups.pyx":203 * rgba[i, 1] = green_lut[idx] * rgba[i, 2] = blue_lut[idx] * rgba[i, 3] = alpha_lut[idx] # <<<<<<<<<<<<<< * * return rgba.reshape(shape) */ __pyx_t_43 = __pyx_v_idx; __pyx_t_44 = __pyx_v_i; __pyx_t_45 = 3; *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_uint8_t *, __pyx_pybuffernd_rgba.rcbuffer->pybuffer.buf, __pyx_t_44, __pyx_pybuffernd_rgba.diminfo[0].strides, __pyx_t_45, __pyx_pybuffernd_rgba.diminfo[1].strides) = (*__Pyx_BufPtrStrided1d(__pyx_t_5numpy_uint8_t *, __pyx_pybuffernd_alpha_lut.rcbuffer->pybuffer.buf, __pyx_t_43, __pyx_pybuffernd_alpha_lut.diminfo[0].strides)); } __pyx_L8:; } /* "chaco/_cython_speedups.pyx":205 * rgba[i, 3] = alpha_lut[idx] * * return rgba.reshape(shape) # <<<<<<<<<<<<<< * * */ __Pyx_XDECREF(__pyx_r); __pyx_t_4 = PyObject_GetAttr(((PyObject *)__pyx_v_rgba), __pyx_n_s__reshape); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 205; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_4); __pyx_t_5 = PyTuple_New(1); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 205; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_5); __Pyx_INCREF(__pyx_v_shape); PyTuple_SET_ITEM(__pyx_t_5, 0, __pyx_v_shape); __Pyx_GIVEREF(__pyx_v_shape); __pyx_t_1 = PyObject_Call(__pyx_t_4, ((PyObject *)__pyx_t_5), NULL); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 205; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_1); __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; __Pyx_DECREF(((PyObject *)__pyx_t_5)); __pyx_t_5 = 0; __pyx_r = __pyx_t_1; __pyx_t_1 = 0; goto __pyx_L0; __pyx_r = Py_None; __Pyx_INCREF(Py_None); goto __pyx_L0; __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_1); __Pyx_XDECREF(__pyx_t_2); __Pyx_XDECREF(__pyx_t_4); __Pyx_XDECREF(__pyx_t_5); { PyObject *__pyx_type, *__pyx_value, *__pyx_tb; __Pyx_ErrFetch(&__pyx_type, &__pyx_value, &__pyx_tb); __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_alpha_lut.rcbuffer->pybuffer); __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_blue_lut.rcbuffer->pybuffer); __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_green_lut.rcbuffer->pybuffer); __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_norm_data.rcbuffer->pybuffer); __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_red_lut.rcbuffer->pybuffer); __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_rgba.rcbuffer->pybuffer); __Pyx_ErrRestore(__pyx_type, __pyx_value, __pyx_tb);} __Pyx_AddTraceback("chaco._cython_speedups.map_colors_uint8", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_r = NULL; goto __pyx_L2; __pyx_L0:; __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_alpha_lut.rcbuffer->pybuffer); __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_blue_lut.rcbuffer->pybuffer); __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_green_lut.rcbuffer->pybuffer); __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_norm_data.rcbuffer->pybuffer); __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_red_lut.rcbuffer->pybuffer); __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_rgba.rcbuffer->pybuffer); __pyx_L2:; __Pyx_XDECREF(__pyx_v_shape); __Pyx_XDECREF((PyObject *)__pyx_v_rgba); __Pyx_XDECREF((PyObject *)__pyx_v_norm_data); __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); return __pyx_r; } /* Python wrapper */ static PyObject *__pyx_pw_5chaco_16_cython_speedups_5apply_selection_fade(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/ static char __pyx_doc_5chaco_16_cython_speedups_4apply_selection_fade[] = "Apply a selection fade to a colormapped image.\n\n Parameters\n ----------\n mapped_image : ndarray of uint8, shape (N,M,4)\n The digitized rgba values\n mask : ndarray of bool, shape (N,M,4)\n The array of masked pixels\n fade_alpha : float\n The alpha value for the fade\n fade_background : rgb888 tuple\n The fade background\n\n "; static PyMethodDef __pyx_mdef_5chaco_16_cython_speedups_5apply_selection_fade = {__Pyx_NAMESTR("apply_selection_fade"), (PyCFunction)__pyx_pw_5chaco_16_cython_speedups_5apply_selection_fade, METH_VARARGS|METH_KEYWORDS, __Pyx_DOCSTR(__pyx_doc_5chaco_16_cython_speedups_4apply_selection_fade)}; static PyObject *__pyx_pw_5chaco_16_cython_speedups_5apply_selection_fade(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds) { PyArrayObject *__pyx_v_mapped_image = 0; PyObject *__pyx_v_mask = 0; PyObject *__pyx_v_fade_alpha = 0; PyObject *__pyx_v_fade_background = 0; static PyObject **__pyx_pyargnames[] = {&__pyx_n_s__mapped_image,&__pyx_n_s__mask,&__pyx_n_s__fade_alpha,&__pyx_n_s__fade_background,0}; PyObject *__pyx_r = 0; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("apply_selection_fade (wrapper)", 0); __pyx_self = __pyx_self; { PyObject* values[4] = {0,0,0,0}; if (unlikely(__pyx_kwds)) { Py_ssize_t kw_args; const Py_ssize_t pos_args = PyTuple_GET_SIZE(__pyx_args); switch (pos_args) { case 4: values[3] = PyTuple_GET_ITEM(__pyx_args, 3); case 3: values[2] = PyTuple_GET_ITEM(__pyx_args, 2); case 2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1); case 1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0); case 0: break; default: goto __pyx_L5_argtuple_error; } kw_args = PyDict_Size(__pyx_kwds); switch (pos_args) { case 0: values[0] = PyDict_GetItem(__pyx_kwds, __pyx_n_s__mapped_image); if (likely(values[0])) kw_args--; else goto __pyx_L5_argtuple_error; case 1: values[1] = PyDict_GetItem(__pyx_kwds, __pyx_n_s__mask); if (likely(values[1])) kw_args--; else { __Pyx_RaiseArgtupleInvalid("apply_selection_fade", 1, 4, 4, 1); {__pyx_filename = __pyx_f[0]; __pyx_lineno = 210; __pyx_clineno = __LINE__; goto __pyx_L3_error;} } case 2: values[2] = PyDict_GetItem(__pyx_kwds, __pyx_n_s__fade_alpha); if (likely(values[2])) kw_args--; else { __Pyx_RaiseArgtupleInvalid("apply_selection_fade", 1, 4, 4, 2); {__pyx_filename = __pyx_f[0]; __pyx_lineno = 210; __pyx_clineno = __LINE__; goto __pyx_L3_error;} } case 3: values[3] = PyDict_GetItem(__pyx_kwds, __pyx_n_s__fade_background); if (likely(values[3])) kw_args--; else { __Pyx_RaiseArgtupleInvalid("apply_selection_fade", 1, 4, 4, 3); {__pyx_filename = __pyx_f[0]; __pyx_lineno = 210; __pyx_clineno = __LINE__; goto __pyx_L3_error;} } } if (unlikely(kw_args > 0)) { if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, pos_args, "apply_selection_fade") < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 210; __pyx_clineno = __LINE__; goto __pyx_L3_error;} } } else if (PyTuple_GET_SIZE(__pyx_args) != 4) { goto __pyx_L5_argtuple_error; } else { values[0] = PyTuple_GET_ITEM(__pyx_args, 0); values[1] = PyTuple_GET_ITEM(__pyx_args, 1); values[2] = PyTuple_GET_ITEM(__pyx_args, 2); values[3] = PyTuple_GET_ITEM(__pyx_args, 3); } __pyx_v_mapped_image = ((PyArrayObject *)values[0]); __pyx_v_mask = values[1]; __pyx_v_fade_alpha = values[2]; __pyx_v_fade_background = values[3]; } goto __pyx_L4_argument_unpacking_done; __pyx_L5_argtuple_error:; __Pyx_RaiseArgtupleInvalid("apply_selection_fade", 1, 4, 4, PyTuple_GET_SIZE(__pyx_args)); {__pyx_filename = __pyx_f[0]; __pyx_lineno = 210; __pyx_clineno = __LINE__; goto __pyx_L3_error;} __pyx_L3_error:; __Pyx_AddTraceback("chaco._cython_speedups.apply_selection_fade", __pyx_clineno, __pyx_lineno, __pyx_filename); __Pyx_RefNannyFinishContext(); return NULL; __pyx_L4_argument_unpacking_done:; if (unlikely(!__Pyx_ArgTypeTest(((PyObject *)__pyx_v_mapped_image), __pyx_ptype_5numpy_ndarray, 0, "mapped_image", 0))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 211; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __pyx_r = __pyx_pf_5chaco_16_cython_speedups_4apply_selection_fade(__pyx_self, __pyx_v_mapped_image, __pyx_v_mask, __pyx_v_fade_alpha, __pyx_v_fade_background); goto __pyx_L0; __pyx_L1_error:; __pyx_r = NULL; __pyx_L0:; __Pyx_RefNannyFinishContext(); return __pyx_r; } /* "chaco/_cython_speedups.pyx":210 * @cython.wraparound(False) * @cython.boundscheck(False) * def apply_selection_fade( # <<<<<<<<<<<<<< * np.ndarray[np.uint8_t, ndim=3] mapped_image not None, * mask, fade_alpha, fade_background): */ static PyObject *__pyx_pf_5chaco_16_cython_speedups_4apply_selection_fade(CYTHON_UNUSED PyObject *__pyx_self, PyArrayObject *__pyx_v_mapped_image, PyObject *__pyx_v_mask, PyObject *__pyx_v_fade_alpha, PyObject *__pyx_v_fade_background) { float __pyx_v_ialpha; float __pyx_v_bg_r; float __pyx_v_bg_g; float __pyx_v_bg_b; int __pyx_v_N; int __pyx_v_M; int __pyx_v_i; int __pyx_v_j; PyArrayObject *__pyx_v_red_lut = 0; PyArrayObject *__pyx_v_green_lut = 0; PyArrayObject *__pyx_v_blue_lut = 0; float __pyx_v_red; float __pyx_v_green; float __pyx_v_blue; float __pyx_v_fade; PyArrayObject *__pyx_v_i8mask = 0; __Pyx_LocalBuf_ND __pyx_pybuffernd_blue_lut; __Pyx_Buffer __pyx_pybuffer_blue_lut; __Pyx_LocalBuf_ND __pyx_pybuffernd_green_lut; __Pyx_Buffer __pyx_pybuffer_green_lut; __Pyx_LocalBuf_ND __pyx_pybuffernd_i8mask; __Pyx_Buffer __pyx_pybuffer_i8mask; __Pyx_LocalBuf_ND __pyx_pybuffernd_mapped_image; __Pyx_Buffer __pyx_pybuffer_mapped_image; __Pyx_LocalBuf_ND __pyx_pybuffernd_red_lut; __Pyx_Buffer __pyx_pybuffer_red_lut; PyObject *__pyx_r = NULL; __Pyx_RefNannyDeclarations PyObject *__pyx_t_1 = NULL; PyObject *__pyx_t_2 = NULL; float __pyx_t_3; double __pyx_t_4; double __pyx_t_5; double __pyx_t_6; PyObject *__pyx_t_7 = NULL; PyArrayObject *__pyx_t_8 = NULL; PyArrayObject *__pyx_t_9 = NULL; PyArrayObject *__pyx_t_10 = NULL; int __pyx_t_11; int __pyx_t_12; int __pyx_t_13; int __pyx_t_14; PyArrayObject *__pyx_t_15 = NULL; int __pyx_t_16; int __pyx_t_17; int __pyx_t_18; int __pyx_t_19; int __pyx_t_20; __pyx_t_5numpy_uint8_t __pyx_t_21; int __pyx_t_22; int __pyx_t_23; long __pyx_t_24; int __pyx_t_25; int __pyx_t_26; long __pyx_t_27; int __pyx_t_28; int __pyx_t_29; long __pyx_t_30; __pyx_t_5numpy_uint8_t __pyx_t_31; int __pyx_t_32; int __pyx_t_33; long __pyx_t_34; int __pyx_t_35; int __pyx_t_36; long __pyx_t_37; __pyx_t_5numpy_uint8_t __pyx_t_38; int __pyx_t_39; int __pyx_t_40; long __pyx_t_41; int __pyx_lineno = 0; const char *__pyx_filename = NULL; int __pyx_clineno = 0; __Pyx_RefNannySetupContext("apply_selection_fade", 0); __pyx_pybuffer_red_lut.pybuffer.buf = NULL; __pyx_pybuffer_red_lut.refcount = 0; __pyx_pybuffernd_red_lut.data = NULL; __pyx_pybuffernd_red_lut.rcbuffer = &__pyx_pybuffer_red_lut; __pyx_pybuffer_green_lut.pybuffer.buf = NULL; __pyx_pybuffer_green_lut.refcount = 0; __pyx_pybuffernd_green_lut.data = NULL; __pyx_pybuffernd_green_lut.rcbuffer = &__pyx_pybuffer_green_lut; __pyx_pybuffer_blue_lut.pybuffer.buf = NULL; __pyx_pybuffer_blue_lut.refcount = 0; __pyx_pybuffernd_blue_lut.data = NULL; __pyx_pybuffernd_blue_lut.rcbuffer = &__pyx_pybuffer_blue_lut; __pyx_pybuffer_i8mask.pybuffer.buf = NULL; __pyx_pybuffer_i8mask.refcount = 0; __pyx_pybuffernd_i8mask.data = NULL; __pyx_pybuffernd_i8mask.rcbuffer = &__pyx_pybuffer_i8mask; __pyx_pybuffer_mapped_image.pybuffer.buf = NULL; __pyx_pybuffer_mapped_image.refcount = 0; __pyx_pybuffernd_mapped_image.data = NULL; __pyx_pybuffernd_mapped_image.rcbuffer = &__pyx_pybuffer_mapped_image; { __Pyx_BufFmt_StackElem __pyx_stack[1]; if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_mapped_image.rcbuffer->pybuffer, (PyObject*)__pyx_v_mapped_image, &__Pyx_TypeInfo_nn___pyx_t_5numpy_uint8_t, PyBUF_FORMAT| PyBUF_STRIDES| PyBUF_WRITABLE, 3, 0, __pyx_stack) == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 210; __pyx_clineno = __LINE__; goto __pyx_L1_error;} } __pyx_pybuffernd_mapped_image.diminfo[0].strides = __pyx_pybuffernd_mapped_image.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_mapped_image.diminfo[0].shape = __pyx_pybuffernd_mapped_image.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_mapped_image.diminfo[1].strides = __pyx_pybuffernd_mapped_image.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_mapped_image.diminfo[1].shape = __pyx_pybuffernd_mapped_image.rcbuffer->pybuffer.shape[1]; __pyx_pybuffernd_mapped_image.diminfo[2].strides = __pyx_pybuffernd_mapped_image.rcbuffer->pybuffer.strides[2]; __pyx_pybuffernd_mapped_image.diminfo[2].shape = __pyx_pybuffernd_mapped_image.rcbuffer->pybuffer.shape[2]; /* "chaco/_cython_speedups.pyx":227 * * ''' * cdef float ialpha = (1.0 - fade_alpha) # <<<<<<<<<<<<<< * ialpha = max(0.0, min(1.0, ialpha)) * */ __pyx_t_1 = PyFloat_FromDouble(1.0); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 227; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_1); __pyx_t_2 = PyNumber_Subtract(__pyx_t_1, __pyx_v_fade_alpha); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 227; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_2); __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; __pyx_t_3 = __pyx_PyFloat_AsFloat(__pyx_t_2); if (unlikely((__pyx_t_3 == (float)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 227; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; __pyx_v_ialpha = __pyx_t_3; /* "chaco/_cython_speedups.pyx":228 * ''' * cdef float ialpha = (1.0 - fade_alpha) * ialpha = max(0.0, min(1.0, ialpha)) # <<<<<<<<<<<<<< * * cdef float bg_r, bg_g, bg_b */ __pyx_t_3 = __pyx_v_ialpha; __pyx_t_4 = 1.0; if ((__pyx_t_3 < __pyx_t_4)) { __pyx_t_5 = __pyx_t_3; } else { __pyx_t_5 = __pyx_t_4; } __pyx_t_4 = __pyx_t_5; __pyx_t_5 = 0.0; if ((__pyx_t_4 > __pyx_t_5)) { __pyx_t_6 = __pyx_t_4; } else { __pyx_t_6 = __pyx_t_5; } __pyx_v_ialpha = __pyx_t_6; /* "chaco/_cython_speedups.pyx":231 * * cdef float bg_r, bg_g, bg_b * bg_r = ialpha * fade_background[0] # <<<<<<<<<<<<<< * bg_r = float_max(0.0, float_min(255.0, bg_r)) * bg_g = ialpha * fade_background[1] */ __pyx_t_2 = PyFloat_FromDouble(__pyx_v_ialpha); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 231; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_2); __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_fade_background, 0, sizeof(long), PyInt_FromLong); if (!__pyx_t_1) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 231; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_1); __pyx_t_7 = PyNumber_Multiply(__pyx_t_2, __pyx_t_1); if (unlikely(!__pyx_t_7)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 231; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_7); __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; __pyx_t_3 = __pyx_PyFloat_AsFloat(__pyx_t_7); if (unlikely((__pyx_t_3 == (float)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 231; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0; __pyx_v_bg_r = __pyx_t_3; /* "chaco/_cython_speedups.pyx":232 * cdef float bg_r, bg_g, bg_b * bg_r = ialpha * fade_background[0] * bg_r = float_max(0.0, float_min(255.0, bg_r)) # <<<<<<<<<<<<<< * bg_g = ialpha * fade_background[1] * bg_g = float_max(0.0, float_min(255.0, bg_g)) */ __pyx_v_bg_r = __pyx_f_5chaco_16_cython_speedups_float_max(0.0, __pyx_f_5chaco_16_cython_speedups_float_min(255.0, __pyx_v_bg_r)); /* "chaco/_cython_speedups.pyx":233 * bg_r = ialpha * fade_background[0] * bg_r = float_max(0.0, float_min(255.0, bg_r)) * bg_g = ialpha * fade_background[1] # <<<<<<<<<<<<<< * bg_g = float_max(0.0, float_min(255.0, bg_g)) * bg_b = ialpha * fade_background[2] */ __pyx_t_7 = PyFloat_FromDouble(__pyx_v_ialpha); if (unlikely(!__pyx_t_7)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 233; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_7); __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_fade_background, 1, sizeof(long), PyInt_FromLong); if (!__pyx_t_1) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 233; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_1); __pyx_t_2 = PyNumber_Multiply(__pyx_t_7, __pyx_t_1); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 233; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_2); __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0; __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; __pyx_t_3 = __pyx_PyFloat_AsFloat(__pyx_t_2); if (unlikely((__pyx_t_3 == (float)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 233; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; __pyx_v_bg_g = __pyx_t_3; /* "chaco/_cython_speedups.pyx":234 * bg_r = float_max(0.0, float_min(255.0, bg_r)) * bg_g = ialpha * fade_background[1] * bg_g = float_max(0.0, float_min(255.0, bg_g)) # <<<<<<<<<<<<<< * bg_b = ialpha * fade_background[2] * bg_b = float_max(0.0, float_min(255.0, bg_b)) */ __pyx_v_bg_g = __pyx_f_5chaco_16_cython_speedups_float_max(0.0, __pyx_f_5chaco_16_cython_speedups_float_min(255.0, __pyx_v_bg_g)); /* "chaco/_cython_speedups.pyx":235 * bg_g = ialpha * fade_background[1] * bg_g = float_max(0.0, float_min(255.0, bg_g)) * bg_b = ialpha * fade_background[2] # <<<<<<<<<<<<<< * bg_b = float_max(0.0, float_min(255.0, bg_b)) * */ __pyx_t_2 = PyFloat_FromDouble(__pyx_v_ialpha); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 235; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_2); __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_fade_background, 2, sizeof(long), PyInt_FromLong); if (!__pyx_t_1) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 235; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_1); __pyx_t_7 = PyNumber_Multiply(__pyx_t_2, __pyx_t_1); if (unlikely(!__pyx_t_7)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 235; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_7); __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; __pyx_t_3 = __pyx_PyFloat_AsFloat(__pyx_t_7); if (unlikely((__pyx_t_3 == (float)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 235; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0; __pyx_v_bg_b = __pyx_t_3; /* "chaco/_cython_speedups.pyx":236 * bg_g = float_max(0.0, float_min(255.0, bg_g)) * bg_b = ialpha * fade_background[2] * bg_b = float_max(0.0, float_min(255.0, bg_b)) # <<<<<<<<<<<<<< * * cdef int N = mapped_image.shape[0] */ __pyx_v_bg_b = __pyx_f_5chaco_16_cython_speedups_float_max(0.0, __pyx_f_5chaco_16_cython_speedups_float_min(255.0, __pyx_v_bg_b)); /* "chaco/_cython_speedups.pyx":238 * bg_b = float_max(0.0, float_min(255.0, bg_b)) * * cdef int N = mapped_image.shape[0] # <<<<<<<<<<<<<< * cdef int M = mapped_image.shape[1] * cdef int i, j */ __pyx_v_N = (__pyx_v_mapped_image->dimensions[0]); /* "chaco/_cython_speedups.pyx":239 * * cdef int N = mapped_image.shape[0] * cdef int M = mapped_image.shape[1] # <<<<<<<<<<<<<< * cdef int i, j * */ __pyx_v_M = (__pyx_v_mapped_image->dimensions[1]); /* "chaco/_cython_speedups.pyx":244 * # Precompute new lookup tables. This is a win when the N*M >> 3*256, * # which is usually the case. * cdef np.ndarray[np.uint8_t] red_lut = np.empty(256, np.uint8) # <<<<<<<<<<<<<< * cdef np.ndarray[np.uint8_t] green_lut = np.empty(256, np.uint8) * cdef np.ndarray[np.uint8_t] blue_lut = np.empty(256, np.uint8) */ __pyx_t_7 = __Pyx_GetName(__pyx_m, __pyx_n_s__np); if (unlikely(!__pyx_t_7)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 244; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_7); __pyx_t_1 = PyObject_GetAttr(__pyx_t_7, __pyx_n_s__empty); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 244; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_1); __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0; __pyx_t_7 = __Pyx_GetName(__pyx_m, __pyx_n_s__np); if (unlikely(!__pyx_t_7)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 244; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_7); __pyx_t_2 = PyObject_GetAttr(__pyx_t_7, __pyx_n_s__uint8); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 244; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_2); __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0; __pyx_t_7 = PyTuple_New(2); if (unlikely(!__pyx_t_7)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 244; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_7); __Pyx_INCREF(__pyx_int_256); PyTuple_SET_ITEM(__pyx_t_7, 0, __pyx_int_256); __Pyx_GIVEREF(__pyx_int_256); PyTuple_SET_ITEM(__pyx_t_7, 1, __pyx_t_2); __Pyx_GIVEREF(__pyx_t_2); __pyx_t_2 = 0; __pyx_t_2 = PyObject_Call(__pyx_t_1, ((PyObject *)__pyx_t_7), NULL); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 244; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_2); __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; __Pyx_DECREF(((PyObject *)__pyx_t_7)); __pyx_t_7 = 0; if (!(likely(((__pyx_t_2) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_2, __pyx_ptype_5numpy_ndarray))))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 244; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __pyx_t_8 = ((PyArrayObject *)__pyx_t_2); { __Pyx_BufFmt_StackElem __pyx_stack[1]; if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_red_lut.rcbuffer->pybuffer, (PyObject*)__pyx_t_8, &__Pyx_TypeInfo_nn___pyx_t_5numpy_uint8_t, PyBUF_FORMAT| PyBUF_STRIDES| PyBUF_WRITABLE, 1, 0, __pyx_stack) == -1)) { __pyx_v_red_lut = ((PyArrayObject *)Py_None); __Pyx_INCREF(Py_None); __pyx_pybuffernd_red_lut.rcbuffer->pybuffer.buf = NULL; {__pyx_filename = __pyx_f[0]; __pyx_lineno = 244; __pyx_clineno = __LINE__; goto __pyx_L1_error;} } else {__pyx_pybuffernd_red_lut.diminfo[0].strides = __pyx_pybuffernd_red_lut.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_red_lut.diminfo[0].shape = __pyx_pybuffernd_red_lut.rcbuffer->pybuffer.shape[0]; } } __pyx_t_8 = 0; __pyx_v_red_lut = ((PyArrayObject *)__pyx_t_2); __pyx_t_2 = 0; /* "chaco/_cython_speedups.pyx":245 * # which is usually the case. * cdef np.ndarray[np.uint8_t] red_lut = np.empty(256, np.uint8) * cdef np.ndarray[np.uint8_t] green_lut = np.empty(256, np.uint8) # <<<<<<<<<<<<<< * cdef np.ndarray[np.uint8_t] blue_lut = np.empty(256, np.uint8) * cdef float red, green, blue, fade */ __pyx_t_2 = __Pyx_GetName(__pyx_m, __pyx_n_s__np); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 245; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_2); __pyx_t_7 = PyObject_GetAttr(__pyx_t_2, __pyx_n_s__empty); if (unlikely(!__pyx_t_7)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 245; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_7); __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; __pyx_t_2 = __Pyx_GetName(__pyx_m, __pyx_n_s__np); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 245; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_2); __pyx_t_1 = PyObject_GetAttr(__pyx_t_2, __pyx_n_s__uint8); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 245; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_1); __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; __pyx_t_2 = PyTuple_New(2); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 245; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_2); __Pyx_INCREF(__pyx_int_256); PyTuple_SET_ITEM(__pyx_t_2, 0, __pyx_int_256); __Pyx_GIVEREF(__pyx_int_256); PyTuple_SET_ITEM(__pyx_t_2, 1, __pyx_t_1); __Pyx_GIVEREF(__pyx_t_1); __pyx_t_1 = 0; __pyx_t_1 = PyObject_Call(__pyx_t_7, ((PyObject *)__pyx_t_2), NULL); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 245; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_1); __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0; __Pyx_DECREF(((PyObject *)__pyx_t_2)); __pyx_t_2 = 0; if (!(likely(((__pyx_t_1) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_1, __pyx_ptype_5numpy_ndarray))))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 245; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __pyx_t_9 = ((PyArrayObject *)__pyx_t_1); { __Pyx_BufFmt_StackElem __pyx_stack[1]; if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_green_lut.rcbuffer->pybuffer, (PyObject*)__pyx_t_9, &__Pyx_TypeInfo_nn___pyx_t_5numpy_uint8_t, PyBUF_FORMAT| PyBUF_STRIDES| PyBUF_WRITABLE, 1, 0, __pyx_stack) == -1)) { __pyx_v_green_lut = ((PyArrayObject *)Py_None); __Pyx_INCREF(Py_None); __pyx_pybuffernd_green_lut.rcbuffer->pybuffer.buf = NULL; {__pyx_filename = __pyx_f[0]; __pyx_lineno = 245; __pyx_clineno = __LINE__; goto __pyx_L1_error;} } else {__pyx_pybuffernd_green_lut.diminfo[0].strides = __pyx_pybuffernd_green_lut.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_green_lut.diminfo[0].shape = __pyx_pybuffernd_green_lut.rcbuffer->pybuffer.shape[0]; } } __pyx_t_9 = 0; __pyx_v_green_lut = ((PyArrayObject *)__pyx_t_1); __pyx_t_1 = 0; /* "chaco/_cython_speedups.pyx":246 * cdef np.ndarray[np.uint8_t] red_lut = np.empty(256, np.uint8) * cdef np.ndarray[np.uint8_t] green_lut = np.empty(256, np.uint8) * cdef np.ndarray[np.uint8_t] blue_lut = np.empty(256, np.uint8) # <<<<<<<<<<<<<< * cdef float red, green, blue, fade * */ __pyx_t_1 = __Pyx_GetName(__pyx_m, __pyx_n_s__np); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 246; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_1); __pyx_t_2 = PyObject_GetAttr(__pyx_t_1, __pyx_n_s__empty); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 246; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_2); __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; __pyx_t_1 = __Pyx_GetName(__pyx_m, __pyx_n_s__np); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 246; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_1); __pyx_t_7 = PyObject_GetAttr(__pyx_t_1, __pyx_n_s__uint8); if (unlikely(!__pyx_t_7)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 246; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_7); __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; __pyx_t_1 = PyTuple_New(2); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 246; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_1); __Pyx_INCREF(__pyx_int_256); PyTuple_SET_ITEM(__pyx_t_1, 0, __pyx_int_256); __Pyx_GIVEREF(__pyx_int_256); PyTuple_SET_ITEM(__pyx_t_1, 1, __pyx_t_7); __Pyx_GIVEREF(__pyx_t_7); __pyx_t_7 = 0; __pyx_t_7 = PyObject_Call(__pyx_t_2, ((PyObject *)__pyx_t_1), NULL); if (unlikely(!__pyx_t_7)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 246; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_7); __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; __Pyx_DECREF(((PyObject *)__pyx_t_1)); __pyx_t_1 = 0; if (!(likely(((__pyx_t_7) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_7, __pyx_ptype_5numpy_ndarray))))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 246; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __pyx_t_10 = ((PyArrayObject *)__pyx_t_7); { __Pyx_BufFmt_StackElem __pyx_stack[1]; if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_blue_lut.rcbuffer->pybuffer, (PyObject*)__pyx_t_10, &__Pyx_TypeInfo_nn___pyx_t_5numpy_uint8_t, PyBUF_FORMAT| PyBUF_STRIDES| PyBUF_WRITABLE, 1, 0, __pyx_stack) == -1)) { __pyx_v_blue_lut = ((PyArrayObject *)Py_None); __Pyx_INCREF(Py_None); __pyx_pybuffernd_blue_lut.rcbuffer->pybuffer.buf = NULL; {__pyx_filename = __pyx_f[0]; __pyx_lineno = 246; __pyx_clineno = __LINE__; goto __pyx_L1_error;} } else {__pyx_pybuffernd_blue_lut.diminfo[0].strides = __pyx_pybuffernd_blue_lut.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_blue_lut.diminfo[0].shape = __pyx_pybuffernd_blue_lut.rcbuffer->pybuffer.shape[0]; } } __pyx_t_10 = 0; __pyx_v_blue_lut = ((PyArrayObject *)__pyx_t_7); __pyx_t_7 = 0; /* "chaco/_cython_speedups.pyx":249 * cdef float red, green, blue, fade * * for i in range(256): # <<<<<<<<<<<<<< * fade = fade_alpha * i * */ for (__pyx_t_11 = 0; __pyx_t_11 < 256; __pyx_t_11+=1) { __pyx_v_i = __pyx_t_11; /* "chaco/_cython_speedups.pyx":250 * * for i in range(256): * fade = fade_alpha * i # <<<<<<<<<<<<<< * * red = fade + bg_r */ __pyx_t_7 = PyInt_FromLong(__pyx_v_i); if (unlikely(!__pyx_t_7)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 250; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_7); __pyx_t_1 = PyNumber_Multiply(__pyx_v_fade_alpha, __pyx_t_7); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 250; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_1); __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0; __pyx_t_3 = __pyx_PyFloat_AsFloat(__pyx_t_1); if (unlikely((__pyx_t_3 == (float)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 250; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; __pyx_v_fade = __pyx_t_3; /* "chaco/_cython_speedups.pyx":252 * fade = fade_alpha * i * * red = fade + bg_r # <<<<<<<<<<<<<< * red = float_max(0.0, float_min(255.0, red)) * red_lut[i] = red */ __pyx_v_red = (__pyx_v_fade + __pyx_v_bg_r); /* "chaco/_cython_speedups.pyx":253 * * red = fade + bg_r * red = float_max(0.0, float_min(255.0, red)) # <<<<<<<<<<<<<< * red_lut[i] = red * */ __pyx_v_red = __pyx_f_5chaco_16_cython_speedups_float_max(0.0, __pyx_f_5chaco_16_cython_speedups_float_min(255.0, __pyx_v_red)); /* "chaco/_cython_speedups.pyx":254 * red = fade + bg_r * red = float_max(0.0, float_min(255.0, red)) * red_lut[i] = red # <<<<<<<<<<<<<< * * green = fade + bg_g */ __pyx_t_12 = __pyx_v_i; *__Pyx_BufPtrStrided1d(__pyx_t_5numpy_uint8_t *, __pyx_pybuffernd_red_lut.rcbuffer->pybuffer.buf, __pyx_t_12, __pyx_pybuffernd_red_lut.diminfo[0].strides) = ((__pyx_t_5numpy_uint8_t)__pyx_v_red); /* "chaco/_cython_speedups.pyx":256 * red_lut[i] = red * * green = fade + bg_g # <<<<<<<<<<<<<< * green = float_max(0.0, float_min(255.0, green)) * green_lut[i] = green */ __pyx_v_green = (__pyx_v_fade + __pyx_v_bg_g); /* "chaco/_cython_speedups.pyx":257 * * green = fade + bg_g * green = float_max(0.0, float_min(255.0, green)) # <<<<<<<<<<<<<< * green_lut[i] = green * */ __pyx_v_green = __pyx_f_5chaco_16_cython_speedups_float_max(0.0, __pyx_f_5chaco_16_cython_speedups_float_min(255.0, __pyx_v_green)); /* "chaco/_cython_speedups.pyx":258 * green = fade + bg_g * green = float_max(0.0, float_min(255.0, green)) * green_lut[i] = green # <<<<<<<<<<<<<< * * blue = fade + bg_b */ __pyx_t_13 = __pyx_v_i; *__Pyx_BufPtrStrided1d(__pyx_t_5numpy_uint8_t *, __pyx_pybuffernd_green_lut.rcbuffer->pybuffer.buf, __pyx_t_13, __pyx_pybuffernd_green_lut.diminfo[0].strides) = ((__pyx_t_5numpy_uint8_t)__pyx_v_green); /* "chaco/_cython_speedups.pyx":260 * green_lut[i] = green * * blue = fade + bg_b # <<<<<<<<<<<<<< * blue = float_max(0.0, float_min(255.0, blue)) * blue_lut[i] = blue */ __pyx_v_blue = (__pyx_v_fade + __pyx_v_bg_b); /* "chaco/_cython_speedups.pyx":261 * * blue = fade + bg_b * blue = float_max(0.0, float_min(255.0, blue)) # <<<<<<<<<<<<<< * blue_lut[i] = blue * */ __pyx_v_blue = __pyx_f_5chaco_16_cython_speedups_float_max(0.0, __pyx_f_5chaco_16_cython_speedups_float_min(255.0, __pyx_v_blue)); /* "chaco/_cython_speedups.pyx":262 * blue = fade + bg_b * blue = float_max(0.0, float_min(255.0, blue)) * blue_lut[i] = blue # <<<<<<<<<<<<<< * * # View the mask as a uint8 so we can benefit from fast lookup. We */ __pyx_t_14 = __pyx_v_i; *__Pyx_BufPtrStrided1d(__pyx_t_5numpy_uint8_t *, __pyx_pybuffernd_blue_lut.rcbuffer->pybuffer.buf, __pyx_t_14, __pyx_pybuffernd_blue_lut.diminfo[0].strides) = ((__pyx_t_5numpy_uint8_t)__pyx_v_blue); } /* "chaco/_cython_speedups.pyx":267 * # would use bools, but these are not well supported. We do this here * # so that the caller doesn't have to deal with it. * cdef np.ndarray[np.uint8_t, ndim=2] i8mask = mask.view(np.uint8) # <<<<<<<<<<<<<< * * */ __pyx_t_1 = PyObject_GetAttr(__pyx_v_mask, __pyx_n_s__view); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 267; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_1); __pyx_t_7 = __Pyx_GetName(__pyx_m, __pyx_n_s__np); if (unlikely(!__pyx_t_7)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 267; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_7); __pyx_t_2 = PyObject_GetAttr(__pyx_t_7, __pyx_n_s__uint8); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 267; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_2); __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0; __pyx_t_7 = PyTuple_New(1); if (unlikely(!__pyx_t_7)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 267; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_7); PyTuple_SET_ITEM(__pyx_t_7, 0, __pyx_t_2); __Pyx_GIVEREF(__pyx_t_2); __pyx_t_2 = 0; __pyx_t_2 = PyObject_Call(__pyx_t_1, ((PyObject *)__pyx_t_7), NULL); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 267; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_2); __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; __Pyx_DECREF(((PyObject *)__pyx_t_7)); __pyx_t_7 = 0; if (!(likely(((__pyx_t_2) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_2, __pyx_ptype_5numpy_ndarray))))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 267; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __pyx_t_15 = ((PyArrayObject *)__pyx_t_2); { __Pyx_BufFmt_StackElem __pyx_stack[1]; if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_i8mask.rcbuffer->pybuffer, (PyObject*)__pyx_t_15, &__Pyx_TypeInfo_nn___pyx_t_5numpy_uint8_t, PyBUF_FORMAT| PyBUF_STRIDES, 2, 0, __pyx_stack) == -1)) { __pyx_v_i8mask = ((PyArrayObject *)Py_None); __Pyx_INCREF(Py_None); __pyx_pybuffernd_i8mask.rcbuffer->pybuffer.buf = NULL; {__pyx_filename = __pyx_f[0]; __pyx_lineno = 267; __pyx_clineno = __LINE__; goto __pyx_L1_error;} } else {__pyx_pybuffernd_i8mask.diminfo[0].strides = __pyx_pybuffernd_i8mask.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_i8mask.diminfo[0].shape = __pyx_pybuffernd_i8mask.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_i8mask.diminfo[1].strides = __pyx_pybuffernd_i8mask.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_i8mask.diminfo[1].shape = __pyx_pybuffernd_i8mask.rcbuffer->pybuffer.shape[1]; } } __pyx_t_15 = 0; __pyx_v_i8mask = ((PyArrayObject *)__pyx_t_2); __pyx_t_2 = 0; /* "chaco/_cython_speedups.pyx":270 * * * for i in range(N): # <<<<<<<<<<<<<< * for j in range(M): * if i8mask[i, j]: */ __pyx_t_11 = __pyx_v_N; for (__pyx_t_16 = 0; __pyx_t_16 < __pyx_t_11; __pyx_t_16+=1) { __pyx_v_i = __pyx_t_16; /* "chaco/_cython_speedups.pyx":271 * * for i in range(N): * for j in range(M): # <<<<<<<<<<<<<< * if i8mask[i, j]: * continue */ __pyx_t_17 = __pyx_v_M; for (__pyx_t_18 = 0; __pyx_t_18 < __pyx_t_17; __pyx_t_18+=1) { __pyx_v_j = __pyx_t_18; /* "chaco/_cython_speedups.pyx":272 * for i in range(N): * for j in range(M): * if i8mask[i, j]: # <<<<<<<<<<<<<< * continue * */ __pyx_t_19 = __pyx_v_i; __pyx_t_20 = __pyx_v_j; __pyx_t_21 = (*__Pyx_BufPtrStrided2d(__pyx_t_5numpy_uint8_t *, __pyx_pybuffernd_i8mask.rcbuffer->pybuffer.buf, __pyx_t_19, __pyx_pybuffernd_i8mask.diminfo[0].strides, __pyx_t_20, __pyx_pybuffernd_i8mask.diminfo[1].strides)); if (__pyx_t_21) { /* "chaco/_cython_speedups.pyx":273 * for j in range(M): * if i8mask[i, j]: * continue # <<<<<<<<<<<<<< * * mapped_image[i, j, 0] = red_lut[mapped_image[i, j, 0]] */ goto __pyx_L7_continue; goto __pyx_L9; } __pyx_L9:; /* "chaco/_cython_speedups.pyx":275 * continue * * mapped_image[i, j, 0] = red_lut[mapped_image[i, j, 0]] # <<<<<<<<<<<<<< * mapped_image[i, j, 1] = green_lut[mapped_image[i, j, 1]] * mapped_image[i, j, 2] = blue_lut[mapped_image[i, j, 2]] */ __pyx_t_22 = __pyx_v_i; __pyx_t_23 = __pyx_v_j; __pyx_t_24 = 0; __pyx_t_21 = (*__Pyx_BufPtrStrided3d(__pyx_t_5numpy_uint8_t *, __pyx_pybuffernd_mapped_image.rcbuffer->pybuffer.buf, __pyx_t_22, __pyx_pybuffernd_mapped_image.diminfo[0].strides, __pyx_t_23, __pyx_pybuffernd_mapped_image.diminfo[1].strides, __pyx_t_24, __pyx_pybuffernd_mapped_image.diminfo[2].strides)); __pyx_t_25 = __pyx_v_i; __pyx_t_26 = __pyx_v_j; __pyx_t_27 = 0; *__Pyx_BufPtrStrided3d(__pyx_t_5numpy_uint8_t *, __pyx_pybuffernd_mapped_image.rcbuffer->pybuffer.buf, __pyx_t_25, __pyx_pybuffernd_mapped_image.diminfo[0].strides, __pyx_t_26, __pyx_pybuffernd_mapped_image.diminfo[1].strides, __pyx_t_27, __pyx_pybuffernd_mapped_image.diminfo[2].strides) = (*__Pyx_BufPtrStrided1d(__pyx_t_5numpy_uint8_t *, __pyx_pybuffernd_red_lut.rcbuffer->pybuffer.buf, __pyx_t_21, __pyx_pybuffernd_red_lut.diminfo[0].strides)); /* "chaco/_cython_speedups.pyx":276 * * mapped_image[i, j, 0] = red_lut[mapped_image[i, j, 0]] * mapped_image[i, j, 1] = green_lut[mapped_image[i, j, 1]] # <<<<<<<<<<<<<< * mapped_image[i, j, 2] = blue_lut[mapped_image[i, j, 2]] */ __pyx_t_28 = __pyx_v_i; __pyx_t_29 = __pyx_v_j; __pyx_t_30 = 1; __pyx_t_31 = (*__Pyx_BufPtrStrided3d(__pyx_t_5numpy_uint8_t *, __pyx_pybuffernd_mapped_image.rcbuffer->pybuffer.buf, __pyx_t_28, __pyx_pybuffernd_mapped_image.diminfo[0].strides, __pyx_t_29, __pyx_pybuffernd_mapped_image.diminfo[1].strides, __pyx_t_30, __pyx_pybuffernd_mapped_image.diminfo[2].strides)); __pyx_t_32 = __pyx_v_i; __pyx_t_33 = __pyx_v_j; __pyx_t_34 = 1; *__Pyx_BufPtrStrided3d(__pyx_t_5numpy_uint8_t *, __pyx_pybuffernd_mapped_image.rcbuffer->pybuffer.buf, __pyx_t_32, __pyx_pybuffernd_mapped_image.diminfo[0].strides, __pyx_t_33, __pyx_pybuffernd_mapped_image.diminfo[1].strides, __pyx_t_34, __pyx_pybuffernd_mapped_image.diminfo[2].strides) = (*__Pyx_BufPtrStrided1d(__pyx_t_5numpy_uint8_t *, __pyx_pybuffernd_green_lut.rcbuffer->pybuffer.buf, __pyx_t_31, __pyx_pybuffernd_green_lut.diminfo[0].strides)); /* "chaco/_cython_speedups.pyx":277 * mapped_image[i, j, 0] = red_lut[mapped_image[i, j, 0]] * mapped_image[i, j, 1] = green_lut[mapped_image[i, j, 1]] * mapped_image[i, j, 2] = blue_lut[mapped_image[i, j, 2]] # <<<<<<<<<<<<<< */ __pyx_t_35 = __pyx_v_i; __pyx_t_36 = __pyx_v_j; __pyx_t_37 = 2; __pyx_t_38 = (*__Pyx_BufPtrStrided3d(__pyx_t_5numpy_uint8_t *, __pyx_pybuffernd_mapped_image.rcbuffer->pybuffer.buf, __pyx_t_35, __pyx_pybuffernd_mapped_image.diminfo[0].strides, __pyx_t_36, __pyx_pybuffernd_mapped_image.diminfo[1].strides, __pyx_t_37, __pyx_pybuffernd_mapped_image.diminfo[2].strides)); __pyx_t_39 = __pyx_v_i; __pyx_t_40 = __pyx_v_j; __pyx_t_41 = 2; *__Pyx_BufPtrStrided3d(__pyx_t_5numpy_uint8_t *, __pyx_pybuffernd_mapped_image.rcbuffer->pybuffer.buf, __pyx_t_39, __pyx_pybuffernd_mapped_image.diminfo[0].strides, __pyx_t_40, __pyx_pybuffernd_mapped_image.diminfo[1].strides, __pyx_t_41, __pyx_pybuffernd_mapped_image.diminfo[2].strides) = (*__Pyx_BufPtrStrided1d(__pyx_t_5numpy_uint8_t *, __pyx_pybuffernd_blue_lut.rcbuffer->pybuffer.buf, __pyx_t_38, __pyx_pybuffernd_blue_lut.diminfo[0].strides)); __pyx_L7_continue:; } } __pyx_r = Py_None; __Pyx_INCREF(Py_None); goto __pyx_L0; __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_1); __Pyx_XDECREF(__pyx_t_2); __Pyx_XDECREF(__pyx_t_7); { PyObject *__pyx_type, *__pyx_value, *__pyx_tb; __Pyx_ErrFetch(&__pyx_type, &__pyx_value, &__pyx_tb); __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_blue_lut.rcbuffer->pybuffer); __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_green_lut.rcbuffer->pybuffer); __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_i8mask.rcbuffer->pybuffer); __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_mapped_image.rcbuffer->pybuffer); __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_red_lut.rcbuffer->pybuffer); __Pyx_ErrRestore(__pyx_type, __pyx_value, __pyx_tb);} __Pyx_AddTraceback("chaco._cython_speedups.apply_selection_fade", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_r = NULL; goto __pyx_L2; __pyx_L0:; __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_blue_lut.rcbuffer->pybuffer); __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_green_lut.rcbuffer->pybuffer); __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_i8mask.rcbuffer->pybuffer); __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_mapped_image.rcbuffer->pybuffer); __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_red_lut.rcbuffer->pybuffer); __pyx_L2:; __Pyx_XDECREF((PyObject *)__pyx_v_red_lut); __Pyx_XDECREF((PyObject *)__pyx_v_green_lut); __Pyx_XDECREF((PyObject *)__pyx_v_blue_lut); __Pyx_XDECREF((PyObject *)__pyx_v_i8mask); __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); return __pyx_r; } /* Python wrapper */ static int __pyx_pw_5numpy_7ndarray_1__getbuffer__(PyObject *__pyx_v_self, Py_buffer *__pyx_v_info, int __pyx_v_flags); /*proto*/ static int __pyx_pw_5numpy_7ndarray_1__getbuffer__(PyObject *__pyx_v_self, Py_buffer *__pyx_v_info, int __pyx_v_flags) { int __pyx_r; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("__getbuffer__ (wrapper)", 0); __pyx_r = __pyx_pf_5numpy_7ndarray___getbuffer__(((PyArrayObject *)__pyx_v_self), ((Py_buffer *)__pyx_v_info), ((int)__pyx_v_flags)); __Pyx_RefNannyFinishContext(); return __pyx_r; } /* "numpy.pxd":193 * # experimental exception made for __getbuffer__ and __releasebuffer__ * # -- the details of this may change. * def __getbuffer__(ndarray self, Py_buffer* info, int flags): # <<<<<<<<<<<<<< * # This implementation of getbuffer is geared towards Cython * # requirements, and does not yet fullfill the PEP. */ static int __pyx_pf_5numpy_7ndarray___getbuffer__(PyArrayObject *__pyx_v_self, Py_buffer *__pyx_v_info, int __pyx_v_flags) { int __pyx_v_copy_shape; int __pyx_v_i; int __pyx_v_ndim; int __pyx_v_endian_detector; int __pyx_v_little_endian; int __pyx_v_t; char *__pyx_v_f; PyArray_Descr *__pyx_v_descr = 0; int __pyx_v_offset; int __pyx_v_hasfields; int __pyx_r; __Pyx_RefNannyDeclarations int __pyx_t_1; int __pyx_t_2; int __pyx_t_3; PyObject *__pyx_t_4 = NULL; int __pyx_t_5; int __pyx_t_6; int __pyx_t_7; PyObject *__pyx_t_8 = NULL; char *__pyx_t_9; int __pyx_lineno = 0; const char *__pyx_filename = NULL; int __pyx_clineno = 0; __Pyx_RefNannySetupContext("__getbuffer__", 0); if (__pyx_v_info != NULL) { __pyx_v_info->obj = Py_None; __Pyx_INCREF(Py_None); __Pyx_GIVEREF(__pyx_v_info->obj); } /* "numpy.pxd":199 * # of flags * * if info == NULL: return # <<<<<<<<<<<<<< * * cdef int copy_shape, i, ndim */ __pyx_t_1 = (__pyx_v_info == NULL); if (__pyx_t_1) { __pyx_r = 0; goto __pyx_L0; goto __pyx_L3; } __pyx_L3:; /* "numpy.pxd":202 * * cdef int copy_shape, i, ndim * cdef int endian_detector = 1 # <<<<<<<<<<<<<< * cdef bint little_endian = ((&endian_detector)[0] != 0) * */ __pyx_v_endian_detector = 1; /* "numpy.pxd":203 * cdef int copy_shape, i, ndim * cdef int endian_detector = 1 * cdef bint little_endian = ((&endian_detector)[0] != 0) # <<<<<<<<<<<<<< * * ndim = PyArray_NDIM(self) */ __pyx_v_little_endian = ((((char *)(&__pyx_v_endian_detector))[0]) != 0); /* "numpy.pxd":205 * cdef bint little_endian = ((&endian_detector)[0] != 0) * * ndim = PyArray_NDIM(self) # <<<<<<<<<<<<<< * * if sizeof(npy_intp) != sizeof(Py_ssize_t): */ __pyx_v_ndim = PyArray_NDIM(__pyx_v_self); /* "numpy.pxd":207 * ndim = PyArray_NDIM(self) * * if sizeof(npy_intp) != sizeof(Py_ssize_t): # <<<<<<<<<<<<<< * copy_shape = 1 * else: */ __pyx_t_1 = ((sizeof(npy_intp)) != (sizeof(Py_ssize_t))); if (__pyx_t_1) { /* "numpy.pxd":208 * * if sizeof(npy_intp) != sizeof(Py_ssize_t): * copy_shape = 1 # <<<<<<<<<<<<<< * else: * copy_shape = 0 */ __pyx_v_copy_shape = 1; goto __pyx_L4; } /*else*/ { /* "numpy.pxd":210 * copy_shape = 1 * else: * copy_shape = 0 # <<<<<<<<<<<<<< * * if ((flags & pybuf.PyBUF_C_CONTIGUOUS == pybuf.PyBUF_C_CONTIGUOUS) */ __pyx_v_copy_shape = 0; } __pyx_L4:; /* "numpy.pxd":212 * copy_shape = 0 * * if ((flags & pybuf.PyBUF_C_CONTIGUOUS == pybuf.PyBUF_C_CONTIGUOUS) # <<<<<<<<<<<<<< * and not PyArray_CHKFLAGS(self, NPY_C_CONTIGUOUS)): * raise ValueError(u"ndarray is not C contiguous") */ __pyx_t_1 = ((__pyx_v_flags & PyBUF_C_CONTIGUOUS) == PyBUF_C_CONTIGUOUS); if (__pyx_t_1) { /* "numpy.pxd":213 * * if ((flags & pybuf.PyBUF_C_CONTIGUOUS == pybuf.PyBUF_C_CONTIGUOUS) * and not PyArray_CHKFLAGS(self, NPY_C_CONTIGUOUS)): # <<<<<<<<<<<<<< * raise ValueError(u"ndarray is not C contiguous") * */ __pyx_t_2 = (!PyArray_CHKFLAGS(__pyx_v_self, NPY_C_CONTIGUOUS)); __pyx_t_3 = __pyx_t_2; } else { __pyx_t_3 = __pyx_t_1; } if (__pyx_t_3) { /* "numpy.pxd":214 * if ((flags & pybuf.PyBUF_C_CONTIGUOUS == pybuf.PyBUF_C_CONTIGUOUS) * and not PyArray_CHKFLAGS(self, NPY_C_CONTIGUOUS)): * raise ValueError(u"ndarray is not C contiguous") # <<<<<<<<<<<<<< * * if ((flags & pybuf.PyBUF_F_CONTIGUOUS == pybuf.PyBUF_F_CONTIGUOUS) */ __pyx_t_4 = PyObject_Call(__pyx_builtin_ValueError, ((PyObject *)__pyx_k_tuple_6), NULL); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 214; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_4); __Pyx_Raise(__pyx_t_4, 0, 0, 0); __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; {__pyx_filename = __pyx_f[1]; __pyx_lineno = 214; __pyx_clineno = __LINE__; goto __pyx_L1_error;} goto __pyx_L5; } __pyx_L5:; /* "numpy.pxd":216 * raise ValueError(u"ndarray is not C contiguous") * * if ((flags & pybuf.PyBUF_F_CONTIGUOUS == pybuf.PyBUF_F_CONTIGUOUS) # <<<<<<<<<<<<<< * and not PyArray_CHKFLAGS(self, NPY_F_CONTIGUOUS)): * raise ValueError(u"ndarray is not Fortran contiguous") */ __pyx_t_3 = ((__pyx_v_flags & PyBUF_F_CONTIGUOUS) == PyBUF_F_CONTIGUOUS); if (__pyx_t_3) { /* "numpy.pxd":217 * * if ((flags & pybuf.PyBUF_F_CONTIGUOUS == pybuf.PyBUF_F_CONTIGUOUS) * and not PyArray_CHKFLAGS(self, NPY_F_CONTIGUOUS)): # <<<<<<<<<<<<<< * raise ValueError(u"ndarray is not Fortran contiguous") * */ __pyx_t_1 = (!PyArray_CHKFLAGS(__pyx_v_self, NPY_F_CONTIGUOUS)); __pyx_t_2 = __pyx_t_1; } else { __pyx_t_2 = __pyx_t_3; } if (__pyx_t_2) { /* "numpy.pxd":218 * if ((flags & pybuf.PyBUF_F_CONTIGUOUS == pybuf.PyBUF_F_CONTIGUOUS) * and not PyArray_CHKFLAGS(self, NPY_F_CONTIGUOUS)): * raise ValueError(u"ndarray is not Fortran contiguous") # <<<<<<<<<<<<<< * * info.buf = PyArray_DATA(self) */ __pyx_t_4 = PyObject_Call(__pyx_builtin_ValueError, ((PyObject *)__pyx_k_tuple_8), NULL); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 218; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_4); __Pyx_Raise(__pyx_t_4, 0, 0, 0); __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; {__pyx_filename = __pyx_f[1]; __pyx_lineno = 218; __pyx_clineno = __LINE__; goto __pyx_L1_error;} goto __pyx_L6; } __pyx_L6:; /* "numpy.pxd":220 * raise ValueError(u"ndarray is not Fortran contiguous") * * info.buf = PyArray_DATA(self) # <<<<<<<<<<<<<< * info.ndim = ndim * if copy_shape: */ __pyx_v_info->buf = PyArray_DATA(__pyx_v_self); /* "numpy.pxd":221 * * info.buf = PyArray_DATA(self) * info.ndim = ndim # <<<<<<<<<<<<<< * if copy_shape: * # Allocate new buffer for strides and shape info. */ __pyx_v_info->ndim = __pyx_v_ndim; /* "numpy.pxd":222 * info.buf = PyArray_DATA(self) * info.ndim = ndim * if copy_shape: # <<<<<<<<<<<<<< * # Allocate new buffer for strides and shape info. * # This is allocated as one block, strides first. */ if (__pyx_v_copy_shape) { /* "numpy.pxd":225 * # Allocate new buffer for strides and shape info. * # This is allocated as one block, strides first. * info.strides = stdlib.malloc(sizeof(Py_ssize_t) * ndim * 2) # <<<<<<<<<<<<<< * info.shape = info.strides + ndim * for i in range(ndim): */ __pyx_v_info->strides = ((Py_ssize_t *)malloc((((sizeof(Py_ssize_t)) * ((size_t)__pyx_v_ndim)) * 2))); /* "numpy.pxd":226 * # This is allocated as one block, strides first. * info.strides = stdlib.malloc(sizeof(Py_ssize_t) * ndim * 2) * info.shape = info.strides + ndim # <<<<<<<<<<<<<< * for i in range(ndim): * info.strides[i] = PyArray_STRIDES(self)[i] */ __pyx_v_info->shape = (__pyx_v_info->strides + __pyx_v_ndim); /* "numpy.pxd":227 * info.strides = stdlib.malloc(sizeof(Py_ssize_t) * ndim * 2) * info.shape = info.strides + ndim * for i in range(ndim): # <<<<<<<<<<<<<< * info.strides[i] = PyArray_STRIDES(self)[i] * info.shape[i] = PyArray_DIMS(self)[i] */ __pyx_t_5 = __pyx_v_ndim; for (__pyx_t_6 = 0; __pyx_t_6 < __pyx_t_5; __pyx_t_6+=1) { __pyx_v_i = __pyx_t_6; /* "numpy.pxd":228 * info.shape = info.strides + ndim * for i in range(ndim): * info.strides[i] = PyArray_STRIDES(self)[i] # <<<<<<<<<<<<<< * info.shape[i] = PyArray_DIMS(self)[i] * else: */ (__pyx_v_info->strides[__pyx_v_i]) = (PyArray_STRIDES(__pyx_v_self)[__pyx_v_i]); /* "numpy.pxd":229 * for i in range(ndim): * info.strides[i] = PyArray_STRIDES(self)[i] * info.shape[i] = PyArray_DIMS(self)[i] # <<<<<<<<<<<<<< * else: * info.strides = PyArray_STRIDES(self) */ (__pyx_v_info->shape[__pyx_v_i]) = (PyArray_DIMS(__pyx_v_self)[__pyx_v_i]); } goto __pyx_L7; } /*else*/ { /* "numpy.pxd":231 * info.shape[i] = PyArray_DIMS(self)[i] * else: * info.strides = PyArray_STRIDES(self) # <<<<<<<<<<<<<< * info.shape = PyArray_DIMS(self) * info.suboffsets = NULL */ __pyx_v_info->strides = ((Py_ssize_t *)PyArray_STRIDES(__pyx_v_self)); /* "numpy.pxd":232 * else: * info.strides = PyArray_STRIDES(self) * info.shape = PyArray_DIMS(self) # <<<<<<<<<<<<<< * info.suboffsets = NULL * info.itemsize = PyArray_ITEMSIZE(self) */ __pyx_v_info->shape = ((Py_ssize_t *)PyArray_DIMS(__pyx_v_self)); } __pyx_L7:; /* "numpy.pxd":233 * info.strides = PyArray_STRIDES(self) * info.shape = PyArray_DIMS(self) * info.suboffsets = NULL # <<<<<<<<<<<<<< * info.itemsize = PyArray_ITEMSIZE(self) * info.readonly = not PyArray_ISWRITEABLE(self) */ __pyx_v_info->suboffsets = NULL; /* "numpy.pxd":234 * info.shape = PyArray_DIMS(self) * info.suboffsets = NULL * info.itemsize = PyArray_ITEMSIZE(self) # <<<<<<<<<<<<<< * info.readonly = not PyArray_ISWRITEABLE(self) * */ __pyx_v_info->itemsize = PyArray_ITEMSIZE(__pyx_v_self); /* "numpy.pxd":235 * info.suboffsets = NULL * info.itemsize = PyArray_ITEMSIZE(self) * info.readonly = not PyArray_ISWRITEABLE(self) # <<<<<<<<<<<<<< * * cdef int t */ __pyx_v_info->readonly = (!PyArray_ISWRITEABLE(__pyx_v_self)); /* "numpy.pxd":238 * * cdef int t * cdef char* f = NULL # <<<<<<<<<<<<<< * cdef dtype descr = self.descr * cdef list stack */ __pyx_v_f = NULL; /* "numpy.pxd":239 * cdef int t * cdef char* f = NULL * cdef dtype descr = self.descr # <<<<<<<<<<<<<< * cdef list stack * cdef int offset */ __Pyx_INCREF(((PyObject *)__pyx_v_self->descr)); __pyx_v_descr = __pyx_v_self->descr; /* "numpy.pxd":243 * cdef int offset * * cdef bint hasfields = PyDataType_HASFIELDS(descr) # <<<<<<<<<<<<<< * * if not hasfields and not copy_shape: */ __pyx_v_hasfields = PyDataType_HASFIELDS(__pyx_v_descr); /* "numpy.pxd":245 * cdef bint hasfields = PyDataType_HASFIELDS(descr) * * if not hasfields and not copy_shape: # <<<<<<<<<<<<<< * # do not call releasebuffer * info.obj = None */ __pyx_t_2 = (!__pyx_v_hasfields); if (__pyx_t_2) { __pyx_t_3 = (!__pyx_v_copy_shape); __pyx_t_1 = __pyx_t_3; } else { __pyx_t_1 = __pyx_t_2; } if (__pyx_t_1) { /* "numpy.pxd":247 * if not hasfields and not copy_shape: * # do not call releasebuffer * info.obj = None # <<<<<<<<<<<<<< * else: * # need to call releasebuffer */ __Pyx_INCREF(Py_None); __Pyx_GIVEREF(Py_None); __Pyx_GOTREF(__pyx_v_info->obj); __Pyx_DECREF(__pyx_v_info->obj); __pyx_v_info->obj = Py_None; goto __pyx_L10; } /*else*/ { /* "numpy.pxd":250 * else: * # need to call releasebuffer * info.obj = self # <<<<<<<<<<<<<< * * if not hasfields: */ __Pyx_INCREF(((PyObject *)__pyx_v_self)); __Pyx_GIVEREF(((PyObject *)__pyx_v_self)); __Pyx_GOTREF(__pyx_v_info->obj); __Pyx_DECREF(__pyx_v_info->obj); __pyx_v_info->obj = ((PyObject *)__pyx_v_self); } __pyx_L10:; /* "numpy.pxd":252 * info.obj = self * * if not hasfields: # <<<<<<<<<<<<<< * t = descr.type_num * if ((descr.byteorder == '>' and little_endian) or */ __pyx_t_1 = (!__pyx_v_hasfields); if (__pyx_t_1) { /* "numpy.pxd":253 * * if not hasfields: * t = descr.type_num # <<<<<<<<<<<<<< * if ((descr.byteorder == '>' and little_endian) or * (descr.byteorder == '<' and not little_endian)): */ __pyx_v_t = __pyx_v_descr->type_num; /* "numpy.pxd":254 * if not hasfields: * t = descr.type_num * if ((descr.byteorder == '>' and little_endian) or # <<<<<<<<<<<<<< * (descr.byteorder == '<' and not little_endian)): * raise ValueError(u"Non-native byte order not supported") */ __pyx_t_1 = (__pyx_v_descr->byteorder == '>'); if (__pyx_t_1) { __pyx_t_2 = __pyx_v_little_endian; } else { __pyx_t_2 = __pyx_t_1; } if (!__pyx_t_2) { /* "numpy.pxd":255 * t = descr.type_num * if ((descr.byteorder == '>' and little_endian) or * (descr.byteorder == '<' and not little_endian)): # <<<<<<<<<<<<<< * raise ValueError(u"Non-native byte order not supported") * if t == NPY_BYTE: f = "b" */ __pyx_t_1 = (__pyx_v_descr->byteorder == '<'); if (__pyx_t_1) { __pyx_t_3 = (!__pyx_v_little_endian); __pyx_t_7 = __pyx_t_3; } else { __pyx_t_7 = __pyx_t_1; } __pyx_t_1 = __pyx_t_7; } else { __pyx_t_1 = __pyx_t_2; } if (__pyx_t_1) { /* "numpy.pxd":256 * if ((descr.byteorder == '>' and little_endian) or * (descr.byteorder == '<' and not little_endian)): * raise ValueError(u"Non-native byte order not supported") # <<<<<<<<<<<<<< * if t == NPY_BYTE: f = "b" * elif t == NPY_UBYTE: f = "B" */ __pyx_t_4 = PyObject_Call(__pyx_builtin_ValueError, ((PyObject *)__pyx_k_tuple_10), NULL); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 256; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_4); __Pyx_Raise(__pyx_t_4, 0, 0, 0); __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; {__pyx_filename = __pyx_f[1]; __pyx_lineno = 256; __pyx_clineno = __LINE__; goto __pyx_L1_error;} goto __pyx_L12; } __pyx_L12:; /* "numpy.pxd":257 * (descr.byteorder == '<' and not little_endian)): * raise ValueError(u"Non-native byte order not supported") * if t == NPY_BYTE: f = "b" # <<<<<<<<<<<<<< * elif t == NPY_UBYTE: f = "B" * elif t == NPY_SHORT: f = "h" */ __pyx_t_1 = (__pyx_v_t == NPY_BYTE); if (__pyx_t_1) { __pyx_v_f = __pyx_k__b; goto __pyx_L13; } /* "numpy.pxd":258 * raise ValueError(u"Non-native byte order not supported") * if t == NPY_BYTE: f = "b" * elif t == NPY_UBYTE: f = "B" # <<<<<<<<<<<<<< * elif t == NPY_SHORT: f = "h" * elif t == NPY_USHORT: f = "H" */ __pyx_t_1 = (__pyx_v_t == NPY_UBYTE); if (__pyx_t_1) { __pyx_v_f = __pyx_k__B; goto __pyx_L13; } /* "numpy.pxd":259 * if t == NPY_BYTE: f = "b" * elif t == NPY_UBYTE: f = "B" * elif t == NPY_SHORT: f = "h" # <<<<<<<<<<<<<< * elif t == NPY_USHORT: f = "H" * elif t == NPY_INT: f = "i" */ __pyx_t_1 = (__pyx_v_t == NPY_SHORT); if (__pyx_t_1) { __pyx_v_f = __pyx_k__h; goto __pyx_L13; } /* "numpy.pxd":260 * elif t == NPY_UBYTE: f = "B" * elif t == NPY_SHORT: f = "h" * elif t == NPY_USHORT: f = "H" # <<<<<<<<<<<<<< * elif t == NPY_INT: f = "i" * elif t == NPY_UINT: f = "I" */ __pyx_t_1 = (__pyx_v_t == NPY_USHORT); if (__pyx_t_1) { __pyx_v_f = __pyx_k__H; goto __pyx_L13; } /* "numpy.pxd":261 * elif t == NPY_SHORT: f = "h" * elif t == NPY_USHORT: f = "H" * elif t == NPY_INT: f = "i" # <<<<<<<<<<<<<< * elif t == NPY_UINT: f = "I" * elif t == NPY_LONG: f = "l" */ __pyx_t_1 = (__pyx_v_t == NPY_INT); if (__pyx_t_1) { __pyx_v_f = __pyx_k__i; goto __pyx_L13; } /* "numpy.pxd":262 * elif t == NPY_USHORT: f = "H" * elif t == NPY_INT: f = "i" * elif t == NPY_UINT: f = "I" # <<<<<<<<<<<<<< * elif t == NPY_LONG: f = "l" * elif t == NPY_ULONG: f = "L" */ __pyx_t_1 = (__pyx_v_t == NPY_UINT); if (__pyx_t_1) { __pyx_v_f = __pyx_k__I; goto __pyx_L13; } /* "numpy.pxd":263 * elif t == NPY_INT: f = "i" * elif t == NPY_UINT: f = "I" * elif t == NPY_LONG: f = "l" # <<<<<<<<<<<<<< * elif t == NPY_ULONG: f = "L" * elif t == NPY_LONGLONG: f = "q" */ __pyx_t_1 = (__pyx_v_t == NPY_LONG); if (__pyx_t_1) { __pyx_v_f = __pyx_k__l; goto __pyx_L13; } /* "numpy.pxd":264 * elif t == NPY_UINT: f = "I" * elif t == NPY_LONG: f = "l" * elif t == NPY_ULONG: f = "L" # <<<<<<<<<<<<<< * elif t == NPY_LONGLONG: f = "q" * elif t == NPY_ULONGLONG: f = "Q" */ __pyx_t_1 = (__pyx_v_t == NPY_ULONG); if (__pyx_t_1) { __pyx_v_f = __pyx_k__L; goto __pyx_L13; } /* "numpy.pxd":265 * elif t == NPY_LONG: f = "l" * elif t == NPY_ULONG: f = "L" * elif t == NPY_LONGLONG: f = "q" # <<<<<<<<<<<<<< * elif t == NPY_ULONGLONG: f = "Q" * elif t == NPY_FLOAT: f = "f" */ __pyx_t_1 = (__pyx_v_t == NPY_LONGLONG); if (__pyx_t_1) { __pyx_v_f = __pyx_k__q; goto __pyx_L13; } /* "numpy.pxd":266 * elif t == NPY_ULONG: f = "L" * elif t == NPY_LONGLONG: f = "q" * elif t == NPY_ULONGLONG: f = "Q" # <<<<<<<<<<<<<< * elif t == NPY_FLOAT: f = "f" * elif t == NPY_DOUBLE: f = "d" */ __pyx_t_1 = (__pyx_v_t == NPY_ULONGLONG); if (__pyx_t_1) { __pyx_v_f = __pyx_k__Q; goto __pyx_L13; } /* "numpy.pxd":267 * elif t == NPY_LONGLONG: f = "q" * elif t == NPY_ULONGLONG: f = "Q" * elif t == NPY_FLOAT: f = "f" # <<<<<<<<<<<<<< * elif t == NPY_DOUBLE: f = "d" * elif t == NPY_LONGDOUBLE: f = "g" */ __pyx_t_1 = (__pyx_v_t == NPY_FLOAT); if (__pyx_t_1) { __pyx_v_f = __pyx_k__f; goto __pyx_L13; } /* "numpy.pxd":268 * elif t == NPY_ULONGLONG: f = "Q" * elif t == NPY_FLOAT: f = "f" * elif t == NPY_DOUBLE: f = "d" # <<<<<<<<<<<<<< * elif t == NPY_LONGDOUBLE: f = "g" * elif t == NPY_CFLOAT: f = "Zf" */ __pyx_t_1 = (__pyx_v_t == NPY_DOUBLE); if (__pyx_t_1) { __pyx_v_f = __pyx_k__d; goto __pyx_L13; } /* "numpy.pxd":269 * elif t == NPY_FLOAT: f = "f" * elif t == NPY_DOUBLE: f = "d" * elif t == NPY_LONGDOUBLE: f = "g" # <<<<<<<<<<<<<< * elif t == NPY_CFLOAT: f = "Zf" * elif t == NPY_CDOUBLE: f = "Zd" */ __pyx_t_1 = (__pyx_v_t == NPY_LONGDOUBLE); if (__pyx_t_1) { __pyx_v_f = __pyx_k__g; goto __pyx_L13; } /* "numpy.pxd":270 * elif t == NPY_DOUBLE: f = "d" * elif t == NPY_LONGDOUBLE: f = "g" * elif t == NPY_CFLOAT: f = "Zf" # <<<<<<<<<<<<<< * elif t == NPY_CDOUBLE: f = "Zd" * elif t == NPY_CLONGDOUBLE: f = "Zg" */ __pyx_t_1 = (__pyx_v_t == NPY_CFLOAT); if (__pyx_t_1) { __pyx_v_f = __pyx_k__Zf; goto __pyx_L13; } /* "numpy.pxd":271 * elif t == NPY_LONGDOUBLE: f = "g" * elif t == NPY_CFLOAT: f = "Zf" * elif t == NPY_CDOUBLE: f = "Zd" # <<<<<<<<<<<<<< * elif t == NPY_CLONGDOUBLE: f = "Zg" * elif t == NPY_OBJECT: f = "O" */ __pyx_t_1 = (__pyx_v_t == NPY_CDOUBLE); if (__pyx_t_1) { __pyx_v_f = __pyx_k__Zd; goto __pyx_L13; } /* "numpy.pxd":272 * elif t == NPY_CFLOAT: f = "Zf" * elif t == NPY_CDOUBLE: f = "Zd" * elif t == NPY_CLONGDOUBLE: f = "Zg" # <<<<<<<<<<<<<< * elif t == NPY_OBJECT: f = "O" * else: */ __pyx_t_1 = (__pyx_v_t == NPY_CLONGDOUBLE); if (__pyx_t_1) { __pyx_v_f = __pyx_k__Zg; goto __pyx_L13; } /* "numpy.pxd":273 * elif t == NPY_CDOUBLE: f = "Zd" * elif t == NPY_CLONGDOUBLE: f = "Zg" * elif t == NPY_OBJECT: f = "O" # <<<<<<<<<<<<<< * else: * raise ValueError(u"unknown dtype code in numpy.pxd (%d)" % t) */ __pyx_t_1 = (__pyx_v_t == NPY_OBJECT); if (__pyx_t_1) { __pyx_v_f = __pyx_k__O; goto __pyx_L13; } /*else*/ { /* "numpy.pxd":275 * elif t == NPY_OBJECT: f = "O" * else: * raise ValueError(u"unknown dtype code in numpy.pxd (%d)" % t) # <<<<<<<<<<<<<< * info.format = f * return */ __pyx_t_4 = PyInt_FromLong(__pyx_v_t); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 275; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_4); __pyx_t_8 = PyNumber_Remainder(((PyObject *)__pyx_kp_u_11), __pyx_t_4); if (unlikely(!__pyx_t_8)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 275; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(((PyObject *)__pyx_t_8)); __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; __pyx_t_4 = PyTuple_New(1); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 275; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_4); PyTuple_SET_ITEM(__pyx_t_4, 0, ((PyObject *)__pyx_t_8)); __Pyx_GIVEREF(((PyObject *)__pyx_t_8)); __pyx_t_8 = 0; __pyx_t_8 = PyObject_Call(__pyx_builtin_ValueError, ((PyObject *)__pyx_t_4), NULL); if (unlikely(!__pyx_t_8)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 275; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_8); __Pyx_DECREF(((PyObject *)__pyx_t_4)); __pyx_t_4 = 0; __Pyx_Raise(__pyx_t_8, 0, 0, 0); __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0; {__pyx_filename = __pyx_f[1]; __pyx_lineno = 275; __pyx_clineno = __LINE__; goto __pyx_L1_error;} } __pyx_L13:; /* "numpy.pxd":276 * else: * raise ValueError(u"unknown dtype code in numpy.pxd (%d)" % t) * info.format = f # <<<<<<<<<<<<<< * return * else: */ __pyx_v_info->format = __pyx_v_f; /* "numpy.pxd":277 * raise ValueError(u"unknown dtype code in numpy.pxd (%d)" % t) * info.format = f * return # <<<<<<<<<<<<<< * else: * info.format = stdlib.malloc(_buffer_format_string_len) */ __pyx_r = 0; goto __pyx_L0; goto __pyx_L11; } /*else*/ { /* "numpy.pxd":279 * return * else: * info.format = stdlib.malloc(_buffer_format_string_len) # <<<<<<<<<<<<<< * info.format[0] = '^' # Native data types, manual alignment * offset = 0 */ __pyx_v_info->format = ((char *)malloc(255)); /* "numpy.pxd":280 * else: * info.format = stdlib.malloc(_buffer_format_string_len) * info.format[0] = '^' # Native data types, manual alignment # <<<<<<<<<<<<<< * offset = 0 * f = _util_dtypestring(descr, info.format + 1, */ (__pyx_v_info->format[0]) = '^'; /* "numpy.pxd":281 * info.format = stdlib.malloc(_buffer_format_string_len) * info.format[0] = '^' # Native data types, manual alignment * offset = 0 # <<<<<<<<<<<<<< * f = _util_dtypestring(descr, info.format + 1, * info.format + _buffer_format_string_len, */ __pyx_v_offset = 0; /* "numpy.pxd":284 * f = _util_dtypestring(descr, info.format + 1, * info.format + _buffer_format_string_len, * &offset) # <<<<<<<<<<<<<< * f[0] = 0 # Terminate format string * */ __pyx_t_9 = __pyx_f_5numpy__util_dtypestring(__pyx_v_descr, (__pyx_v_info->format + 1), (__pyx_v_info->format + 255), (&__pyx_v_offset)); if (unlikely(__pyx_t_9 == NULL)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 282; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __pyx_v_f = __pyx_t_9; /* "numpy.pxd":285 * info.format + _buffer_format_string_len, * &offset) * f[0] = 0 # Terminate format string # <<<<<<<<<<<<<< * * def __releasebuffer__(ndarray self, Py_buffer* info): */ (__pyx_v_f[0]) = 0; } __pyx_L11:; __pyx_r = 0; goto __pyx_L0; __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_4); __Pyx_XDECREF(__pyx_t_8); __Pyx_AddTraceback("numpy.ndarray.__getbuffer__", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_r = -1; if (__pyx_v_info != NULL && __pyx_v_info->obj != NULL) { __Pyx_GOTREF(__pyx_v_info->obj); __Pyx_DECREF(__pyx_v_info->obj); __pyx_v_info->obj = NULL; } goto __pyx_L2; __pyx_L0:; if (__pyx_v_info != NULL && __pyx_v_info->obj == Py_None) { __Pyx_GOTREF(Py_None); __Pyx_DECREF(Py_None); __pyx_v_info->obj = NULL; } __pyx_L2:; __Pyx_XDECREF((PyObject *)__pyx_v_descr); __Pyx_RefNannyFinishContext(); return __pyx_r; } /* Python wrapper */ static void __pyx_pw_5numpy_7ndarray_3__releasebuffer__(PyObject *__pyx_v_self, Py_buffer *__pyx_v_info); /*proto*/ static void __pyx_pw_5numpy_7ndarray_3__releasebuffer__(PyObject *__pyx_v_self, Py_buffer *__pyx_v_info) { __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("__releasebuffer__ (wrapper)", 0); __pyx_pf_5numpy_7ndarray_2__releasebuffer__(((PyArrayObject *)__pyx_v_self), ((Py_buffer *)__pyx_v_info)); __Pyx_RefNannyFinishContext(); } /* "numpy.pxd":287 * f[0] = 0 # Terminate format string * * def __releasebuffer__(ndarray self, Py_buffer* info): # <<<<<<<<<<<<<< * if PyArray_HASFIELDS(self): * stdlib.free(info.format) */ static void __pyx_pf_5numpy_7ndarray_2__releasebuffer__(PyArrayObject *__pyx_v_self, Py_buffer *__pyx_v_info) { __Pyx_RefNannyDeclarations int __pyx_t_1; __Pyx_RefNannySetupContext("__releasebuffer__", 0); /* "numpy.pxd":288 * * def __releasebuffer__(ndarray self, Py_buffer* info): * if PyArray_HASFIELDS(self): # <<<<<<<<<<<<<< * stdlib.free(info.format) * if sizeof(npy_intp) != sizeof(Py_ssize_t): */ __pyx_t_1 = PyArray_HASFIELDS(__pyx_v_self); if (__pyx_t_1) { /* "numpy.pxd":289 * def __releasebuffer__(ndarray self, Py_buffer* info): * if PyArray_HASFIELDS(self): * stdlib.free(info.format) # <<<<<<<<<<<<<< * if sizeof(npy_intp) != sizeof(Py_ssize_t): * stdlib.free(info.strides) */ free(__pyx_v_info->format); goto __pyx_L3; } __pyx_L3:; /* "numpy.pxd":290 * if PyArray_HASFIELDS(self): * stdlib.free(info.format) * if sizeof(npy_intp) != sizeof(Py_ssize_t): # <<<<<<<<<<<<<< * stdlib.free(info.strides) * # info.shape was stored after info.strides in the same block */ __pyx_t_1 = ((sizeof(npy_intp)) != (sizeof(Py_ssize_t))); if (__pyx_t_1) { /* "numpy.pxd":291 * stdlib.free(info.format) * if sizeof(npy_intp) != sizeof(Py_ssize_t): * stdlib.free(info.strides) # <<<<<<<<<<<<<< * # info.shape was stored after info.strides in the same block * */ free(__pyx_v_info->strides); goto __pyx_L4; } __pyx_L4:; __Pyx_RefNannyFinishContext(); } /* "numpy.pxd":767 * ctypedef npy_cdouble complex_t * * cdef inline object PyArray_MultiIterNew1(a): # <<<<<<<<<<<<<< * return PyArray_MultiIterNew(1, a) * */ static CYTHON_INLINE PyObject *__pyx_f_5numpy_PyArray_MultiIterNew1(PyObject *__pyx_v_a) { PyObject *__pyx_r = NULL; __Pyx_RefNannyDeclarations PyObject *__pyx_t_1 = NULL; int __pyx_lineno = 0; const char *__pyx_filename = NULL; int __pyx_clineno = 0; __Pyx_RefNannySetupContext("PyArray_MultiIterNew1", 0); /* "numpy.pxd":768 * * cdef inline object PyArray_MultiIterNew1(a): * return PyArray_MultiIterNew(1, a) # <<<<<<<<<<<<<< * * cdef inline object PyArray_MultiIterNew2(a, b): */ __Pyx_XDECREF(__pyx_r); __pyx_t_1 = PyArray_MultiIterNew(1, ((void *)__pyx_v_a)); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 768; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_1); __pyx_r = __pyx_t_1; __pyx_t_1 = 0; goto __pyx_L0; __pyx_r = Py_None; __Pyx_INCREF(Py_None); goto __pyx_L0; __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_1); __Pyx_AddTraceback("numpy.PyArray_MultiIterNew1", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_r = 0; __pyx_L0:; __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); return __pyx_r; } /* "numpy.pxd":770 * return PyArray_MultiIterNew(1, a) * * cdef inline object PyArray_MultiIterNew2(a, b): # <<<<<<<<<<<<<< * return PyArray_MultiIterNew(2, a, b) * */ static CYTHON_INLINE PyObject *__pyx_f_5numpy_PyArray_MultiIterNew2(PyObject *__pyx_v_a, PyObject *__pyx_v_b) { PyObject *__pyx_r = NULL; __Pyx_RefNannyDeclarations PyObject *__pyx_t_1 = NULL; int __pyx_lineno = 0; const char *__pyx_filename = NULL; int __pyx_clineno = 0; __Pyx_RefNannySetupContext("PyArray_MultiIterNew2", 0); /* "numpy.pxd":771 * * cdef inline object PyArray_MultiIterNew2(a, b): * return PyArray_MultiIterNew(2, a, b) # <<<<<<<<<<<<<< * * cdef inline object PyArray_MultiIterNew3(a, b, c): */ __Pyx_XDECREF(__pyx_r); __pyx_t_1 = PyArray_MultiIterNew(2, ((void *)__pyx_v_a), ((void *)__pyx_v_b)); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 771; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_1); __pyx_r = __pyx_t_1; __pyx_t_1 = 0; goto __pyx_L0; __pyx_r = Py_None; __Pyx_INCREF(Py_None); goto __pyx_L0; __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_1); __Pyx_AddTraceback("numpy.PyArray_MultiIterNew2", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_r = 0; __pyx_L0:; __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); return __pyx_r; } /* "numpy.pxd":773 * return PyArray_MultiIterNew(2, a, b) * * cdef inline object PyArray_MultiIterNew3(a, b, c): # <<<<<<<<<<<<<< * return PyArray_MultiIterNew(3, a, b, c) * */ static CYTHON_INLINE PyObject *__pyx_f_5numpy_PyArray_MultiIterNew3(PyObject *__pyx_v_a, PyObject *__pyx_v_b, PyObject *__pyx_v_c) { PyObject *__pyx_r = NULL; __Pyx_RefNannyDeclarations PyObject *__pyx_t_1 = NULL; int __pyx_lineno = 0; const char *__pyx_filename = NULL; int __pyx_clineno = 0; __Pyx_RefNannySetupContext("PyArray_MultiIterNew3", 0); /* "numpy.pxd":774 * * cdef inline object PyArray_MultiIterNew3(a, b, c): * return PyArray_MultiIterNew(3, a, b, c) # <<<<<<<<<<<<<< * * cdef inline object PyArray_MultiIterNew4(a, b, c, d): */ __Pyx_XDECREF(__pyx_r); __pyx_t_1 = PyArray_MultiIterNew(3, ((void *)__pyx_v_a), ((void *)__pyx_v_b), ((void *)__pyx_v_c)); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 774; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_1); __pyx_r = __pyx_t_1; __pyx_t_1 = 0; goto __pyx_L0; __pyx_r = Py_None; __Pyx_INCREF(Py_None); goto __pyx_L0; __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_1); __Pyx_AddTraceback("numpy.PyArray_MultiIterNew3", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_r = 0; __pyx_L0:; __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); return __pyx_r; } /* "numpy.pxd":776 * return PyArray_MultiIterNew(3, a, b, c) * * cdef inline object PyArray_MultiIterNew4(a, b, c, d): # <<<<<<<<<<<<<< * return PyArray_MultiIterNew(4, a, b, c, d) * */ static CYTHON_INLINE PyObject *__pyx_f_5numpy_PyArray_MultiIterNew4(PyObject *__pyx_v_a, PyObject *__pyx_v_b, PyObject *__pyx_v_c, PyObject *__pyx_v_d) { PyObject *__pyx_r = NULL; __Pyx_RefNannyDeclarations PyObject *__pyx_t_1 = NULL; int __pyx_lineno = 0; const char *__pyx_filename = NULL; int __pyx_clineno = 0; __Pyx_RefNannySetupContext("PyArray_MultiIterNew4", 0); /* "numpy.pxd":777 * * cdef inline object PyArray_MultiIterNew4(a, b, c, d): * return PyArray_MultiIterNew(4, a, b, c, d) # <<<<<<<<<<<<<< * * cdef inline object PyArray_MultiIterNew5(a, b, c, d, e): */ __Pyx_XDECREF(__pyx_r); __pyx_t_1 = PyArray_MultiIterNew(4, ((void *)__pyx_v_a), ((void *)__pyx_v_b), ((void *)__pyx_v_c), ((void *)__pyx_v_d)); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 777; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_1); __pyx_r = __pyx_t_1; __pyx_t_1 = 0; goto __pyx_L0; __pyx_r = Py_None; __Pyx_INCREF(Py_None); goto __pyx_L0; __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_1); __Pyx_AddTraceback("numpy.PyArray_MultiIterNew4", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_r = 0; __pyx_L0:; __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); return __pyx_r; } /* "numpy.pxd":779 * return PyArray_MultiIterNew(4, a, b, c, d) * * cdef inline object PyArray_MultiIterNew5(a, b, c, d, e): # <<<<<<<<<<<<<< * return PyArray_MultiIterNew(5, a, b, c, d, e) * */ static CYTHON_INLINE PyObject *__pyx_f_5numpy_PyArray_MultiIterNew5(PyObject *__pyx_v_a, PyObject *__pyx_v_b, PyObject *__pyx_v_c, PyObject *__pyx_v_d, PyObject *__pyx_v_e) { PyObject *__pyx_r = NULL; __Pyx_RefNannyDeclarations PyObject *__pyx_t_1 = NULL; int __pyx_lineno = 0; const char *__pyx_filename = NULL; int __pyx_clineno = 0; __Pyx_RefNannySetupContext("PyArray_MultiIterNew5", 0); /* "numpy.pxd":780 * * cdef inline object PyArray_MultiIterNew5(a, b, c, d, e): * return PyArray_MultiIterNew(5, a, b, c, d, e) # <<<<<<<<<<<<<< * * cdef inline char* _util_dtypestring(dtype descr, char* f, char* end, int* offset) except NULL: */ __Pyx_XDECREF(__pyx_r); __pyx_t_1 = PyArray_MultiIterNew(5, ((void *)__pyx_v_a), ((void *)__pyx_v_b), ((void *)__pyx_v_c), ((void *)__pyx_v_d), ((void *)__pyx_v_e)); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 780; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_1); __pyx_r = __pyx_t_1; __pyx_t_1 = 0; goto __pyx_L0; __pyx_r = Py_None; __Pyx_INCREF(Py_None); goto __pyx_L0; __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_1); __Pyx_AddTraceback("numpy.PyArray_MultiIterNew5", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_r = 0; __pyx_L0:; __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); return __pyx_r; } /* "numpy.pxd":782 * return PyArray_MultiIterNew(5, a, b, c, d, e) * * cdef inline char* _util_dtypestring(dtype descr, char* f, char* end, int* offset) except NULL: # <<<<<<<<<<<<<< * # Recursive utility function used in __getbuffer__ to get format * # string. The new location in the format string is returned. */ static CYTHON_INLINE char *__pyx_f_5numpy__util_dtypestring(PyArray_Descr *__pyx_v_descr, char *__pyx_v_f, char *__pyx_v_end, int *__pyx_v_offset) { PyArray_Descr *__pyx_v_child = 0; int __pyx_v_endian_detector; int __pyx_v_little_endian; PyObject *__pyx_v_fields = 0; PyObject *__pyx_v_childname = NULL; PyObject *__pyx_v_new_offset = NULL; PyObject *__pyx_v_t = NULL; char *__pyx_r; __Pyx_RefNannyDeclarations PyObject *__pyx_t_1 = NULL; Py_ssize_t __pyx_t_2; PyObject *__pyx_t_3 = NULL; PyObject *__pyx_t_4 = NULL; PyObject *__pyx_t_5 = NULL; int __pyx_t_6; int __pyx_t_7; int __pyx_t_8; int __pyx_t_9; long __pyx_t_10; char *__pyx_t_11; int __pyx_lineno = 0; const char *__pyx_filename = NULL; int __pyx_clineno = 0; __Pyx_RefNannySetupContext("_util_dtypestring", 0); /* "numpy.pxd":789 * cdef int delta_offset * cdef tuple i * cdef int endian_detector = 1 # <<<<<<<<<<<<<< * cdef bint little_endian = ((&endian_detector)[0] != 0) * cdef tuple fields */ __pyx_v_endian_detector = 1; /* "numpy.pxd":790 * cdef tuple i * cdef int endian_detector = 1 * cdef bint little_endian = ((&endian_detector)[0] != 0) # <<<<<<<<<<<<<< * cdef tuple fields * */ __pyx_v_little_endian = ((((char *)(&__pyx_v_endian_detector))[0]) != 0); /* "numpy.pxd":793 * cdef tuple fields * * for childname in descr.names: # <<<<<<<<<<<<<< * fields = descr.fields[childname] * child, new_offset = fields */ if (unlikely(((PyObject *)__pyx_v_descr->names) == Py_None)) { PyErr_SetString(PyExc_TypeError, "'NoneType' object is not iterable"); {__pyx_filename = __pyx_f[1]; __pyx_lineno = 793; __pyx_clineno = __LINE__; goto __pyx_L1_error;} } __pyx_t_1 = ((PyObject *)__pyx_v_descr->names); __Pyx_INCREF(__pyx_t_1); __pyx_t_2 = 0; for (;;) { if (__pyx_t_2 >= PyTuple_GET_SIZE(__pyx_t_1)) break; __pyx_t_3 = PyTuple_GET_ITEM(__pyx_t_1, __pyx_t_2); __Pyx_INCREF(__pyx_t_3); __pyx_t_2++; __Pyx_XDECREF(__pyx_v_childname); __pyx_v_childname = __pyx_t_3; __pyx_t_3 = 0; /* "numpy.pxd":794 * * for childname in descr.names: * fields = descr.fields[childname] # <<<<<<<<<<<<<< * child, new_offset = fields * */ __pyx_t_3 = PyObject_GetItem(__pyx_v_descr->fields, __pyx_v_childname); if (!__pyx_t_3) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 794; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_3); if (!(likely(PyTuple_CheckExact(__pyx_t_3))||((__pyx_t_3) == Py_None)||(PyErr_Format(PyExc_TypeError, "Expected tuple, got %.200s", Py_TYPE(__pyx_t_3)->tp_name), 0))) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 794; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_XDECREF(((PyObject *)__pyx_v_fields)); __pyx_v_fields = ((PyObject*)__pyx_t_3); __pyx_t_3 = 0; /* "numpy.pxd":795 * for childname in descr.names: * fields = descr.fields[childname] * child, new_offset = fields # <<<<<<<<<<<<<< * * if (end - f) - (new_offset - offset[0]) < 15: */ if (likely(PyTuple_CheckExact(((PyObject *)__pyx_v_fields)))) { PyObject* sequence = ((PyObject *)__pyx_v_fields); if (unlikely(PyTuple_GET_SIZE(sequence) != 2)) { if (PyTuple_GET_SIZE(sequence) > 2) __Pyx_RaiseTooManyValuesError(2); else __Pyx_RaiseNeedMoreValuesError(PyTuple_GET_SIZE(sequence)); {__pyx_filename = __pyx_f[1]; __pyx_lineno = 795; __pyx_clineno = __LINE__; goto __pyx_L1_error;} } __pyx_t_3 = PyTuple_GET_ITEM(sequence, 0); __pyx_t_4 = PyTuple_GET_ITEM(sequence, 1); __Pyx_INCREF(__pyx_t_3); __Pyx_INCREF(__pyx_t_4); } else { __Pyx_UnpackTupleError(((PyObject *)__pyx_v_fields), 2); {__pyx_filename = __pyx_f[1]; __pyx_lineno = 795; __pyx_clineno = __LINE__; goto __pyx_L1_error;} } if (!(likely(((__pyx_t_3) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_3, __pyx_ptype_5numpy_dtype))))) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 795; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_XDECREF(((PyObject *)__pyx_v_child)); __pyx_v_child = ((PyArray_Descr *)__pyx_t_3); __pyx_t_3 = 0; __Pyx_XDECREF(__pyx_v_new_offset); __pyx_v_new_offset = __pyx_t_4; __pyx_t_4 = 0; /* "numpy.pxd":797 * child, new_offset = fields * * if (end - f) - (new_offset - offset[0]) < 15: # <<<<<<<<<<<<<< * raise RuntimeError(u"Format string allocated too short, see comment in numpy.pxd") * */ __pyx_t_4 = PyInt_FromLong((__pyx_v_end - __pyx_v_f)); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 797; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_4); __pyx_t_3 = PyInt_FromLong((__pyx_v_offset[0])); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 797; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_3); __pyx_t_5 = PyNumber_Subtract(__pyx_v_new_offset, __pyx_t_3); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 797; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_5); __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; __pyx_t_3 = PyNumber_Subtract(__pyx_t_4, __pyx_t_5); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 797; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_3); __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; __pyx_t_5 = PyObject_RichCompare(__pyx_t_3, __pyx_int_15, Py_LT); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 797; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_5); __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; __pyx_t_6 = __Pyx_PyObject_IsTrue(__pyx_t_5); if (unlikely(__pyx_t_6 < 0)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 797; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; if (__pyx_t_6) { /* "numpy.pxd":798 * * if (end - f) - (new_offset - offset[0]) < 15: * raise RuntimeError(u"Format string allocated too short, see comment in numpy.pxd") # <<<<<<<<<<<<<< * * if ((child.byteorder == '>' and little_endian) or */ __pyx_t_5 = PyObject_Call(__pyx_builtin_RuntimeError, ((PyObject *)__pyx_k_tuple_13), NULL); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 798; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_5); __Pyx_Raise(__pyx_t_5, 0, 0, 0); __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; {__pyx_filename = __pyx_f[1]; __pyx_lineno = 798; __pyx_clineno = __LINE__; goto __pyx_L1_error;} goto __pyx_L5; } __pyx_L5:; /* "numpy.pxd":800 * raise RuntimeError(u"Format string allocated too short, see comment in numpy.pxd") * * if ((child.byteorder == '>' and little_endian) or # <<<<<<<<<<<<<< * (child.byteorder == '<' and not little_endian)): * raise ValueError(u"Non-native byte order not supported") */ __pyx_t_6 = (__pyx_v_child->byteorder == '>'); if (__pyx_t_6) { __pyx_t_7 = __pyx_v_little_endian; } else { __pyx_t_7 = __pyx_t_6; } if (!__pyx_t_7) { /* "numpy.pxd":801 * * if ((child.byteorder == '>' and little_endian) or * (child.byteorder == '<' and not little_endian)): # <<<<<<<<<<<<<< * raise ValueError(u"Non-native byte order not supported") * # One could encode it in the format string and have Cython */ __pyx_t_6 = (__pyx_v_child->byteorder == '<'); if (__pyx_t_6) { __pyx_t_8 = (!__pyx_v_little_endian); __pyx_t_9 = __pyx_t_8; } else { __pyx_t_9 = __pyx_t_6; } __pyx_t_6 = __pyx_t_9; } else { __pyx_t_6 = __pyx_t_7; } if (__pyx_t_6) { /* "numpy.pxd":802 * if ((child.byteorder == '>' and little_endian) or * (child.byteorder == '<' and not little_endian)): * raise ValueError(u"Non-native byte order not supported") # <<<<<<<<<<<<<< * # One could encode it in the format string and have Cython * # complain instead, BUT: < and > in format strings also imply */ __pyx_t_5 = PyObject_Call(__pyx_builtin_ValueError, ((PyObject *)__pyx_k_tuple_14), NULL); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 802; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_5); __Pyx_Raise(__pyx_t_5, 0, 0, 0); __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; {__pyx_filename = __pyx_f[1]; __pyx_lineno = 802; __pyx_clineno = __LINE__; goto __pyx_L1_error;} goto __pyx_L6; } __pyx_L6:; /* "numpy.pxd":812 * * # Output padding bytes * while offset[0] < new_offset: # <<<<<<<<<<<<<< * f[0] = 120 # "x"; pad byte * f += 1 */ while (1) { __pyx_t_5 = PyInt_FromLong((__pyx_v_offset[0])); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 812; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_5); __pyx_t_3 = PyObject_RichCompare(__pyx_t_5, __pyx_v_new_offset, Py_LT); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 812; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_3); __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; __pyx_t_6 = __Pyx_PyObject_IsTrue(__pyx_t_3); if (unlikely(__pyx_t_6 < 0)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 812; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; if (!__pyx_t_6) break; /* "numpy.pxd":813 * # Output padding bytes * while offset[0] < new_offset: * f[0] = 120 # "x"; pad byte # <<<<<<<<<<<<<< * f += 1 * offset[0] += 1 */ (__pyx_v_f[0]) = 120; /* "numpy.pxd":814 * while offset[0] < new_offset: * f[0] = 120 # "x"; pad byte * f += 1 # <<<<<<<<<<<<<< * offset[0] += 1 * */ __pyx_v_f = (__pyx_v_f + 1); /* "numpy.pxd":815 * f[0] = 120 # "x"; pad byte * f += 1 * offset[0] += 1 # <<<<<<<<<<<<<< * * offset[0] += child.itemsize */ __pyx_t_10 = 0; (__pyx_v_offset[__pyx_t_10]) = ((__pyx_v_offset[__pyx_t_10]) + 1); } /* "numpy.pxd":817 * offset[0] += 1 * * offset[0] += child.itemsize # <<<<<<<<<<<<<< * * if not PyDataType_HASFIELDS(child): */ __pyx_t_10 = 0; (__pyx_v_offset[__pyx_t_10]) = ((__pyx_v_offset[__pyx_t_10]) + __pyx_v_child->elsize); /* "numpy.pxd":819 * offset[0] += child.itemsize * * if not PyDataType_HASFIELDS(child): # <<<<<<<<<<<<<< * t = child.type_num * if end - f < 5: */ __pyx_t_6 = (!PyDataType_HASFIELDS(__pyx_v_child)); if (__pyx_t_6) { /* "numpy.pxd":820 * * if not PyDataType_HASFIELDS(child): * t = child.type_num # <<<<<<<<<<<<<< * if end - f < 5: * raise RuntimeError(u"Format string allocated too short.") */ __pyx_t_3 = PyInt_FromLong(__pyx_v_child->type_num); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 820; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_3); __Pyx_XDECREF(__pyx_v_t); __pyx_v_t = __pyx_t_3; __pyx_t_3 = 0; /* "numpy.pxd":821 * if not PyDataType_HASFIELDS(child): * t = child.type_num * if end - f < 5: # <<<<<<<<<<<<<< * raise RuntimeError(u"Format string allocated too short.") * */ __pyx_t_6 = ((__pyx_v_end - __pyx_v_f) < 5); if (__pyx_t_6) { /* "numpy.pxd":822 * t = child.type_num * if end - f < 5: * raise RuntimeError(u"Format string allocated too short.") # <<<<<<<<<<<<<< * * # Until ticket #99 is fixed, use integers to avoid warnings */ __pyx_t_3 = PyObject_Call(__pyx_builtin_RuntimeError, ((PyObject *)__pyx_k_tuple_16), NULL); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 822; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_3); __Pyx_Raise(__pyx_t_3, 0, 0, 0); __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; {__pyx_filename = __pyx_f[1]; __pyx_lineno = 822; __pyx_clineno = __LINE__; goto __pyx_L1_error;} goto __pyx_L10; } __pyx_L10:; /* "numpy.pxd":825 * * # Until ticket #99 is fixed, use integers to avoid warnings * if t == NPY_BYTE: f[0] = 98 #"b" # <<<<<<<<<<<<<< * elif t == NPY_UBYTE: f[0] = 66 #"B" * elif t == NPY_SHORT: f[0] = 104 #"h" */ __pyx_t_3 = PyInt_FromLong(NPY_BYTE); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 825; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_3); __pyx_t_5 = PyObject_RichCompare(__pyx_v_t, __pyx_t_3, Py_EQ); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 825; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_5); __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; __pyx_t_6 = __Pyx_PyObject_IsTrue(__pyx_t_5); if (unlikely(__pyx_t_6 < 0)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 825; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; if (__pyx_t_6) { (__pyx_v_f[0]) = 98; goto __pyx_L11; } /* "numpy.pxd":826 * # Until ticket #99 is fixed, use integers to avoid warnings * if t == NPY_BYTE: f[0] = 98 #"b" * elif t == NPY_UBYTE: f[0] = 66 #"B" # <<<<<<<<<<<<<< * elif t == NPY_SHORT: f[0] = 104 #"h" * elif t == NPY_USHORT: f[0] = 72 #"H" */ __pyx_t_5 = PyInt_FromLong(NPY_UBYTE); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 826; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_5); __pyx_t_3 = PyObject_RichCompare(__pyx_v_t, __pyx_t_5, Py_EQ); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 826; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_3); __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; __pyx_t_6 = __Pyx_PyObject_IsTrue(__pyx_t_3); if (unlikely(__pyx_t_6 < 0)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 826; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; if (__pyx_t_6) { (__pyx_v_f[0]) = 66; goto __pyx_L11; } /* "numpy.pxd":827 * if t == NPY_BYTE: f[0] = 98 #"b" * elif t == NPY_UBYTE: f[0] = 66 #"B" * elif t == NPY_SHORT: f[0] = 104 #"h" # <<<<<<<<<<<<<< * elif t == NPY_USHORT: f[0] = 72 #"H" * elif t == NPY_INT: f[0] = 105 #"i" */ __pyx_t_3 = PyInt_FromLong(NPY_SHORT); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 827; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_3); __pyx_t_5 = PyObject_RichCompare(__pyx_v_t, __pyx_t_3, Py_EQ); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 827; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_5); __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; __pyx_t_6 = __Pyx_PyObject_IsTrue(__pyx_t_5); if (unlikely(__pyx_t_6 < 0)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 827; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; if (__pyx_t_6) { (__pyx_v_f[0]) = 104; goto __pyx_L11; } /* "numpy.pxd":828 * elif t == NPY_UBYTE: f[0] = 66 #"B" * elif t == NPY_SHORT: f[0] = 104 #"h" * elif t == NPY_USHORT: f[0] = 72 #"H" # <<<<<<<<<<<<<< * elif t == NPY_INT: f[0] = 105 #"i" * elif t == NPY_UINT: f[0] = 73 #"I" */ __pyx_t_5 = PyInt_FromLong(NPY_USHORT); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 828; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_5); __pyx_t_3 = PyObject_RichCompare(__pyx_v_t, __pyx_t_5, Py_EQ); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 828; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_3); __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; __pyx_t_6 = __Pyx_PyObject_IsTrue(__pyx_t_3); if (unlikely(__pyx_t_6 < 0)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 828; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; if (__pyx_t_6) { (__pyx_v_f[0]) = 72; goto __pyx_L11; } /* "numpy.pxd":829 * elif t == NPY_SHORT: f[0] = 104 #"h" * elif t == NPY_USHORT: f[0] = 72 #"H" * elif t == NPY_INT: f[0] = 105 #"i" # <<<<<<<<<<<<<< * elif t == NPY_UINT: f[0] = 73 #"I" * elif t == NPY_LONG: f[0] = 108 #"l" */ __pyx_t_3 = PyInt_FromLong(NPY_INT); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 829; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_3); __pyx_t_5 = PyObject_RichCompare(__pyx_v_t, __pyx_t_3, Py_EQ); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 829; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_5); __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; __pyx_t_6 = __Pyx_PyObject_IsTrue(__pyx_t_5); if (unlikely(__pyx_t_6 < 0)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 829; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; if (__pyx_t_6) { (__pyx_v_f[0]) = 105; goto __pyx_L11; } /* "numpy.pxd":830 * elif t == NPY_USHORT: f[0] = 72 #"H" * elif t == NPY_INT: f[0] = 105 #"i" * elif t == NPY_UINT: f[0] = 73 #"I" # <<<<<<<<<<<<<< * elif t == NPY_LONG: f[0] = 108 #"l" * elif t == NPY_ULONG: f[0] = 76 #"L" */ __pyx_t_5 = PyInt_FromLong(NPY_UINT); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 830; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_5); __pyx_t_3 = PyObject_RichCompare(__pyx_v_t, __pyx_t_5, Py_EQ); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 830; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_3); __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; __pyx_t_6 = __Pyx_PyObject_IsTrue(__pyx_t_3); if (unlikely(__pyx_t_6 < 0)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 830; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; if (__pyx_t_6) { (__pyx_v_f[0]) = 73; goto __pyx_L11; } /* "numpy.pxd":831 * elif t == NPY_INT: f[0] = 105 #"i" * elif t == NPY_UINT: f[0] = 73 #"I" * elif t == NPY_LONG: f[0] = 108 #"l" # <<<<<<<<<<<<<< * elif t == NPY_ULONG: f[0] = 76 #"L" * elif t == NPY_LONGLONG: f[0] = 113 #"q" */ __pyx_t_3 = PyInt_FromLong(NPY_LONG); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 831; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_3); __pyx_t_5 = PyObject_RichCompare(__pyx_v_t, __pyx_t_3, Py_EQ); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 831; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_5); __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; __pyx_t_6 = __Pyx_PyObject_IsTrue(__pyx_t_5); if (unlikely(__pyx_t_6 < 0)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 831; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; if (__pyx_t_6) { (__pyx_v_f[0]) = 108; goto __pyx_L11; } /* "numpy.pxd":832 * elif t == NPY_UINT: f[0] = 73 #"I" * elif t == NPY_LONG: f[0] = 108 #"l" * elif t == NPY_ULONG: f[0] = 76 #"L" # <<<<<<<<<<<<<< * elif t == NPY_LONGLONG: f[0] = 113 #"q" * elif t == NPY_ULONGLONG: f[0] = 81 #"Q" */ __pyx_t_5 = PyInt_FromLong(NPY_ULONG); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 832; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_5); __pyx_t_3 = PyObject_RichCompare(__pyx_v_t, __pyx_t_5, Py_EQ); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 832; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_3); __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; __pyx_t_6 = __Pyx_PyObject_IsTrue(__pyx_t_3); if (unlikely(__pyx_t_6 < 0)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 832; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; if (__pyx_t_6) { (__pyx_v_f[0]) = 76; goto __pyx_L11; } /* "numpy.pxd":833 * elif t == NPY_LONG: f[0] = 108 #"l" * elif t == NPY_ULONG: f[0] = 76 #"L" * elif t == NPY_LONGLONG: f[0] = 113 #"q" # <<<<<<<<<<<<<< * elif t == NPY_ULONGLONG: f[0] = 81 #"Q" * elif t == NPY_FLOAT: f[0] = 102 #"f" */ __pyx_t_3 = PyInt_FromLong(NPY_LONGLONG); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 833; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_3); __pyx_t_5 = PyObject_RichCompare(__pyx_v_t, __pyx_t_3, Py_EQ); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 833; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_5); __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; __pyx_t_6 = __Pyx_PyObject_IsTrue(__pyx_t_5); if (unlikely(__pyx_t_6 < 0)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 833; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; if (__pyx_t_6) { (__pyx_v_f[0]) = 113; goto __pyx_L11; } /* "numpy.pxd":834 * elif t == NPY_ULONG: f[0] = 76 #"L" * elif t == NPY_LONGLONG: f[0] = 113 #"q" * elif t == NPY_ULONGLONG: f[0] = 81 #"Q" # <<<<<<<<<<<<<< * elif t == NPY_FLOAT: f[0] = 102 #"f" * elif t == NPY_DOUBLE: f[0] = 100 #"d" */ __pyx_t_5 = PyInt_FromLong(NPY_ULONGLONG); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 834; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_5); __pyx_t_3 = PyObject_RichCompare(__pyx_v_t, __pyx_t_5, Py_EQ); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 834; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_3); __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; __pyx_t_6 = __Pyx_PyObject_IsTrue(__pyx_t_3); if (unlikely(__pyx_t_6 < 0)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 834; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; if (__pyx_t_6) { (__pyx_v_f[0]) = 81; goto __pyx_L11; } /* "numpy.pxd":835 * elif t == NPY_LONGLONG: f[0] = 113 #"q" * elif t == NPY_ULONGLONG: f[0] = 81 #"Q" * elif t == NPY_FLOAT: f[0] = 102 #"f" # <<<<<<<<<<<<<< * elif t == NPY_DOUBLE: f[0] = 100 #"d" * elif t == NPY_LONGDOUBLE: f[0] = 103 #"g" */ __pyx_t_3 = PyInt_FromLong(NPY_FLOAT); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 835; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_3); __pyx_t_5 = PyObject_RichCompare(__pyx_v_t, __pyx_t_3, Py_EQ); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 835; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_5); __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; __pyx_t_6 = __Pyx_PyObject_IsTrue(__pyx_t_5); if (unlikely(__pyx_t_6 < 0)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 835; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; if (__pyx_t_6) { (__pyx_v_f[0]) = 102; goto __pyx_L11; } /* "numpy.pxd":836 * elif t == NPY_ULONGLONG: f[0] = 81 #"Q" * elif t == NPY_FLOAT: f[0] = 102 #"f" * elif t == NPY_DOUBLE: f[0] = 100 #"d" # <<<<<<<<<<<<<< * elif t == NPY_LONGDOUBLE: f[0] = 103 #"g" * elif t == NPY_CFLOAT: f[0] = 90; f[1] = 102; f += 1 # Zf */ __pyx_t_5 = PyInt_FromLong(NPY_DOUBLE); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 836; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_5); __pyx_t_3 = PyObject_RichCompare(__pyx_v_t, __pyx_t_5, Py_EQ); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 836; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_3); __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; __pyx_t_6 = __Pyx_PyObject_IsTrue(__pyx_t_3); if (unlikely(__pyx_t_6 < 0)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 836; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; if (__pyx_t_6) { (__pyx_v_f[0]) = 100; goto __pyx_L11; } /* "numpy.pxd":837 * elif t == NPY_FLOAT: f[0] = 102 #"f" * elif t == NPY_DOUBLE: f[0] = 100 #"d" * elif t == NPY_LONGDOUBLE: f[0] = 103 #"g" # <<<<<<<<<<<<<< * elif t == NPY_CFLOAT: f[0] = 90; f[1] = 102; f += 1 # Zf * elif t == NPY_CDOUBLE: f[0] = 90; f[1] = 100; f += 1 # Zd */ __pyx_t_3 = PyInt_FromLong(NPY_LONGDOUBLE); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 837; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_3); __pyx_t_5 = PyObject_RichCompare(__pyx_v_t, __pyx_t_3, Py_EQ); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 837; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_5); __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; __pyx_t_6 = __Pyx_PyObject_IsTrue(__pyx_t_5); if (unlikely(__pyx_t_6 < 0)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 837; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; if (__pyx_t_6) { (__pyx_v_f[0]) = 103; goto __pyx_L11; } /* "numpy.pxd":838 * elif t == NPY_DOUBLE: f[0] = 100 #"d" * elif t == NPY_LONGDOUBLE: f[0] = 103 #"g" * elif t == NPY_CFLOAT: f[0] = 90; f[1] = 102; f += 1 # Zf # <<<<<<<<<<<<<< * elif t == NPY_CDOUBLE: f[0] = 90; f[1] = 100; f += 1 # Zd * elif t == NPY_CLONGDOUBLE: f[0] = 90; f[1] = 103; f += 1 # Zg */ __pyx_t_5 = PyInt_FromLong(NPY_CFLOAT); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 838; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_5); __pyx_t_3 = PyObject_RichCompare(__pyx_v_t, __pyx_t_5, Py_EQ); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 838; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_3); __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; __pyx_t_6 = __Pyx_PyObject_IsTrue(__pyx_t_3); if (unlikely(__pyx_t_6 < 0)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 838; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; if (__pyx_t_6) { (__pyx_v_f[0]) = 90; (__pyx_v_f[1]) = 102; __pyx_v_f = (__pyx_v_f + 1); goto __pyx_L11; } /* "numpy.pxd":839 * elif t == NPY_LONGDOUBLE: f[0] = 103 #"g" * elif t == NPY_CFLOAT: f[0] = 90; f[1] = 102; f += 1 # Zf * elif t == NPY_CDOUBLE: f[0] = 90; f[1] = 100; f += 1 # Zd # <<<<<<<<<<<<<< * elif t == NPY_CLONGDOUBLE: f[0] = 90; f[1] = 103; f += 1 # Zg * elif t == NPY_OBJECT: f[0] = 79 #"O" */ __pyx_t_3 = PyInt_FromLong(NPY_CDOUBLE); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 839; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_3); __pyx_t_5 = PyObject_RichCompare(__pyx_v_t, __pyx_t_3, Py_EQ); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 839; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_5); __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; __pyx_t_6 = __Pyx_PyObject_IsTrue(__pyx_t_5); if (unlikely(__pyx_t_6 < 0)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 839; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; if (__pyx_t_6) { (__pyx_v_f[0]) = 90; (__pyx_v_f[1]) = 100; __pyx_v_f = (__pyx_v_f + 1); goto __pyx_L11; } /* "numpy.pxd":840 * elif t == NPY_CFLOAT: f[0] = 90; f[1] = 102; f += 1 # Zf * elif t == NPY_CDOUBLE: f[0] = 90; f[1] = 100; f += 1 # Zd * elif t == NPY_CLONGDOUBLE: f[0] = 90; f[1] = 103; f += 1 # Zg # <<<<<<<<<<<<<< * elif t == NPY_OBJECT: f[0] = 79 #"O" * else: */ __pyx_t_5 = PyInt_FromLong(NPY_CLONGDOUBLE); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 840; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_5); __pyx_t_3 = PyObject_RichCompare(__pyx_v_t, __pyx_t_5, Py_EQ); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 840; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_3); __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; __pyx_t_6 = __Pyx_PyObject_IsTrue(__pyx_t_3); if (unlikely(__pyx_t_6 < 0)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 840; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; if (__pyx_t_6) { (__pyx_v_f[0]) = 90; (__pyx_v_f[1]) = 103; __pyx_v_f = (__pyx_v_f + 1); goto __pyx_L11; } /* "numpy.pxd":841 * elif t == NPY_CDOUBLE: f[0] = 90; f[1] = 100; f += 1 # Zd * elif t == NPY_CLONGDOUBLE: f[0] = 90; f[1] = 103; f += 1 # Zg * elif t == NPY_OBJECT: f[0] = 79 #"O" # <<<<<<<<<<<<<< * else: * raise ValueError(u"unknown dtype code in numpy.pxd (%d)" % t) */ __pyx_t_3 = PyInt_FromLong(NPY_OBJECT); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 841; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_3); __pyx_t_5 = PyObject_RichCompare(__pyx_v_t, __pyx_t_3, Py_EQ); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 841; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_5); __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; __pyx_t_6 = __Pyx_PyObject_IsTrue(__pyx_t_5); if (unlikely(__pyx_t_6 < 0)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 841; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; if (__pyx_t_6) { (__pyx_v_f[0]) = 79; goto __pyx_L11; } /*else*/ { /* "numpy.pxd":843 * elif t == NPY_OBJECT: f[0] = 79 #"O" * else: * raise ValueError(u"unknown dtype code in numpy.pxd (%d)" % t) # <<<<<<<<<<<<<< * f += 1 * else: */ __pyx_t_5 = PyNumber_Remainder(((PyObject *)__pyx_kp_u_11), __pyx_v_t); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 843; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(((PyObject *)__pyx_t_5)); __pyx_t_3 = PyTuple_New(1); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 843; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_3); PyTuple_SET_ITEM(__pyx_t_3, 0, ((PyObject *)__pyx_t_5)); __Pyx_GIVEREF(((PyObject *)__pyx_t_5)); __pyx_t_5 = 0; __pyx_t_5 = PyObject_Call(__pyx_builtin_ValueError, ((PyObject *)__pyx_t_3), NULL); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 843; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_5); __Pyx_DECREF(((PyObject *)__pyx_t_3)); __pyx_t_3 = 0; __Pyx_Raise(__pyx_t_5, 0, 0, 0); __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; {__pyx_filename = __pyx_f[1]; __pyx_lineno = 843; __pyx_clineno = __LINE__; goto __pyx_L1_error;} } __pyx_L11:; /* "numpy.pxd":844 * else: * raise ValueError(u"unknown dtype code in numpy.pxd (%d)" % t) * f += 1 # <<<<<<<<<<<<<< * else: * # Cython ignores struct boundary information ("T{...}"), */ __pyx_v_f = (__pyx_v_f + 1); goto __pyx_L9; } /*else*/ { /* "numpy.pxd":848 * # Cython ignores struct boundary information ("T{...}"), * # so don't output it * f = _util_dtypestring(child, f, end, offset) # <<<<<<<<<<<<<< * return f * */ __pyx_t_11 = __pyx_f_5numpy__util_dtypestring(__pyx_v_child, __pyx_v_f, __pyx_v_end, __pyx_v_offset); if (unlikely(__pyx_t_11 == NULL)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 848; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __pyx_v_f = __pyx_t_11; } __pyx_L9:; } __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; /* "numpy.pxd":849 * # so don't output it * f = _util_dtypestring(child, f, end, offset) * return f # <<<<<<<<<<<<<< * * */ __pyx_r = __pyx_v_f; goto __pyx_L0; __pyx_r = 0; goto __pyx_L0; __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_1); __Pyx_XDECREF(__pyx_t_3); __Pyx_XDECREF(__pyx_t_4); __Pyx_XDECREF(__pyx_t_5); __Pyx_AddTraceback("numpy._util_dtypestring", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_r = NULL; __pyx_L0:; __Pyx_XDECREF((PyObject *)__pyx_v_child); __Pyx_XDECREF(__pyx_v_fields); __Pyx_XDECREF(__pyx_v_childname); __Pyx_XDECREF(__pyx_v_new_offset); __Pyx_XDECREF(__pyx_v_t); __Pyx_RefNannyFinishContext(); return __pyx_r; } /* "numpy.pxd":964 * * * cdef inline void set_array_base(ndarray arr, object base): # <<<<<<<<<<<<<< * cdef PyObject* baseptr * if base is None: */ static CYTHON_INLINE void __pyx_f_5numpy_set_array_base(PyArrayObject *__pyx_v_arr, PyObject *__pyx_v_base) { PyObject *__pyx_v_baseptr; __Pyx_RefNannyDeclarations int __pyx_t_1; __Pyx_RefNannySetupContext("set_array_base", 0); /* "numpy.pxd":966 * cdef inline void set_array_base(ndarray arr, object base): * cdef PyObject* baseptr * if base is None: # <<<<<<<<<<<<<< * baseptr = NULL * else: */ __pyx_t_1 = (__pyx_v_base == Py_None); if (__pyx_t_1) { /* "numpy.pxd":967 * cdef PyObject* baseptr * if base is None: * baseptr = NULL # <<<<<<<<<<<<<< * else: * Py_INCREF(base) # important to do this before decref below! */ __pyx_v_baseptr = NULL; goto __pyx_L3; } /*else*/ { /* "numpy.pxd":969 * baseptr = NULL * else: * Py_INCREF(base) # important to do this before decref below! # <<<<<<<<<<<<<< * baseptr = base * Py_XDECREF(arr.base) */ Py_INCREF(__pyx_v_base); /* "numpy.pxd":970 * else: * Py_INCREF(base) # important to do this before decref below! * baseptr = base # <<<<<<<<<<<<<< * Py_XDECREF(arr.base) * arr.base = baseptr */ __pyx_v_baseptr = ((PyObject *)__pyx_v_base); } __pyx_L3:; /* "numpy.pxd":971 * Py_INCREF(base) # important to do this before decref below! * baseptr = base * Py_XDECREF(arr.base) # <<<<<<<<<<<<<< * arr.base = baseptr * */ Py_XDECREF(__pyx_v_arr->base); /* "numpy.pxd":972 * baseptr = base * Py_XDECREF(arr.base) * arr.base = baseptr # <<<<<<<<<<<<<< * * cdef inline object get_array_base(ndarray arr): */ __pyx_v_arr->base = __pyx_v_baseptr; __Pyx_RefNannyFinishContext(); } /* "numpy.pxd":974 * arr.base = baseptr * * cdef inline object get_array_base(ndarray arr): # <<<<<<<<<<<<<< * if arr.base is NULL: * return None */ static CYTHON_INLINE PyObject *__pyx_f_5numpy_get_array_base(PyArrayObject *__pyx_v_arr) { PyObject *__pyx_r = NULL; __Pyx_RefNannyDeclarations int __pyx_t_1; __Pyx_RefNannySetupContext("get_array_base", 0); /* "numpy.pxd":975 * * cdef inline object get_array_base(ndarray arr): * if arr.base is NULL: # <<<<<<<<<<<<<< * return None * else: */ __pyx_t_1 = (__pyx_v_arr->base == NULL); if (__pyx_t_1) { /* "numpy.pxd":976 * cdef inline object get_array_base(ndarray arr): * if arr.base is NULL: * return None # <<<<<<<<<<<<<< * else: * return arr.base */ __Pyx_XDECREF(__pyx_r); __Pyx_INCREF(Py_None); __pyx_r = Py_None; goto __pyx_L0; goto __pyx_L3; } /*else*/ { /* "numpy.pxd":978 * return None * else: * return arr.base # <<<<<<<<<<<<<< */ __Pyx_XDECREF(__pyx_r); __Pyx_INCREF(((PyObject *)__pyx_v_arr->base)); __pyx_r = ((PyObject *)__pyx_v_arr->base); goto __pyx_L0; } __pyx_L3:; __pyx_r = Py_None; __Pyx_INCREF(Py_None); __pyx_L0:; __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); return __pyx_r; } static PyMethodDef __pyx_methods[] = { {0, 0, 0, 0} }; #if PY_MAJOR_VERSION >= 3 static struct PyModuleDef __pyx_moduledef = { PyModuleDef_HEAD_INIT, __Pyx_NAMESTR("_cython_speedups"), 0, /* m_doc */ -1, /* m_size */ __pyx_methods /* m_methods */, NULL, /* m_reload */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL /* m_free */ }; #endif static __Pyx_StringTabEntry __pyx_string_tab[] = { {&__pyx_kp_u_11, __pyx_k_11, sizeof(__pyx_k_11), 0, 1, 0, 0}, {&__pyx_kp_u_12, __pyx_k_12, sizeof(__pyx_k_12), 0, 1, 0, 0}, {&__pyx_kp_u_15, __pyx_k_15, sizeof(__pyx_k_15), 0, 1, 0, 0}, {&__pyx_kp_s_19, __pyx_k_19, sizeof(__pyx_k_19), 0, 0, 1, 0}, {&__pyx_n_s_20, __pyx_k_20, sizeof(__pyx_k_20), 0, 0, 1, 1}, {&__pyx_n_s_25, __pyx_k_25, sizeof(__pyx_k_25), 0, 0, 1, 1}, {&__pyx_kp_u_5, __pyx_k_5, sizeof(__pyx_k_5), 0, 1, 0, 0}, {&__pyx_kp_u_7, __pyx_k_7, sizeof(__pyx_k_7), 0, 1, 0, 0}, {&__pyx_kp_u_9, __pyx_k_9, sizeof(__pyx_k_9), 0, 1, 0, 0}, {&__pyx_n_s__M, __pyx_k__M, sizeof(__pyx_k__M), 0, 0, 1, 1}, {&__pyx_n_s__N, __pyx_k__N, sizeof(__pyx_k__N), 0, 0, 1, 1}, {&__pyx_n_s__RuntimeError, __pyx_k__RuntimeError, sizeof(__pyx_k__RuntimeError), 0, 0, 1, 1}, {&__pyx_n_s__ValueError, __pyx_k__ValueError, sizeof(__pyx_k__ValueError), 0, 0, 1, 1}, {&__pyx_n_s____main__, __pyx_k____main__, sizeof(__pyx_k____main__), 0, 0, 1, 1}, {&__pyx_n_s____test__, __pyx_k____test__, sizeof(__pyx_k____test__), 0, 0, 1, 1}, {&__pyx_n_s__alpha, __pyx_k__alpha, sizeof(__pyx_k__alpha), 0, 0, 1, 1}, {&__pyx_n_s__alpha_lut, __pyx_k__alpha_lut, sizeof(__pyx_k__alpha_lut), 0, 0, 1, 1}, {&__pyx_n_s__bg_b, __pyx_k__bg_b, sizeof(__pyx_k__bg_b), 0, 0, 1, 1}, {&__pyx_n_s__bg_g, __pyx_k__bg_g, sizeof(__pyx_k__bg_g), 0, 0, 1, 1}, {&__pyx_n_s__bg_r, __pyx_k__bg_r, sizeof(__pyx_k__bg_r), 0, 0, 1, 1}, {&__pyx_n_s__blue, __pyx_k__blue, sizeof(__pyx_k__blue), 0, 0, 1, 1}, {&__pyx_n_s__blue_lut, __pyx_k__blue_lut, sizeof(__pyx_k__blue_lut), 0, 0, 1, 1}, {&__pyx_n_s__data_array, __pyx_k__data_array, sizeof(__pyx_k__data_array), 0, 0, 1, 1}, {&__pyx_n_s__empty, __pyx_k__empty, sizeof(__pyx_k__empty), 0, 0, 1, 1}, {&__pyx_n_s__fade, __pyx_k__fade, sizeof(__pyx_k__fade), 0, 0, 1, 1}, {&__pyx_n_s__fade_alpha, __pyx_k__fade_alpha, sizeof(__pyx_k__fade_alpha), 0, 0, 1, 1}, {&__pyx_n_s__fade_background, __pyx_k__fade_background, sizeof(__pyx_k__fade_background), 0, 0, 1, 1}, {&__pyx_n_s__flat, __pyx_k__flat, sizeof(__pyx_k__flat), 0, 0, 1, 1}, {&__pyx_n_s__float32, __pyx_k__float32, sizeof(__pyx_k__float32), 0, 0, 1, 1}, {&__pyx_n_s__green, __pyx_k__green, sizeof(__pyx_k__green), 0, 0, 1, 1}, {&__pyx_n_s__green_lut, __pyx_k__green_lut, sizeof(__pyx_k__green_lut), 0, 0, 1, 1}, {&__pyx_n_s__high, __pyx_k__high, sizeof(__pyx_k__high), 0, 0, 1, 1}, {&__pyx_n_s__i, __pyx_k__i, sizeof(__pyx_k__i), 0, 0, 1, 1}, {&__pyx_n_s__i8mask, __pyx_k__i8mask, sizeof(__pyx_k__i8mask), 0, 0, 1, 1}, {&__pyx_n_s__ialpha, __pyx_k__ialpha, sizeof(__pyx_k__ialpha), 0, 0, 1, 1}, {&__pyx_n_s__idx, __pyx_k__idx, sizeof(__pyx_k__idx), 0, 0, 1, 1}, {&__pyx_n_s__isinf, __pyx_k__isinf, sizeof(__pyx_k__isinf), 0, 0, 1, 1}, {&__pyx_n_s__j, __pyx_k__j, sizeof(__pyx_k__j), 0, 0, 1, 1}, {&__pyx_n_s__low, __pyx_k__low, sizeof(__pyx_k__low), 0, 0, 1, 1}, {&__pyx_n_s__map_colors, __pyx_k__map_colors, sizeof(__pyx_k__map_colors), 0, 0, 1, 1}, {&__pyx_n_s__map_colors_uint8, __pyx_k__map_colors_uint8, sizeof(__pyx_k__map_colors_uint8), 0, 0, 1, 1}, {&__pyx_n_s__mapped_image, __pyx_k__mapped_image, sizeof(__pyx_k__mapped_image), 0, 0, 1, 1}, {&__pyx_n_s__mask, __pyx_k__mask, sizeof(__pyx_k__mask), 0, 0, 1, 1}, {&__pyx_n_s__norm_data, __pyx_k__norm_data, sizeof(__pyx_k__norm_data), 0, 0, 1, 1}, {&__pyx_n_s__norm_value, __pyx_k__norm_value, sizeof(__pyx_k__norm_value), 0, 0, 1, 1}, {&__pyx_n_s__np, __pyx_k__np, sizeof(__pyx_k__np), 0, 0, 1, 1}, {&__pyx_n_s__numpy, __pyx_k__numpy, sizeof(__pyx_k__numpy), 0, 0, 1, 1}, {&__pyx_n_s__range, __pyx_k__range, sizeof(__pyx_k__range), 0, 0, 1, 1}, {&__pyx_n_s__range_diff, __pyx_k__range_diff, sizeof(__pyx_k__range_diff), 0, 0, 1, 1}, {&__pyx_n_s__red, __pyx_k__red, sizeof(__pyx_k__red), 0, 0, 1, 1}, {&__pyx_n_s__red_lut, __pyx_k__red_lut, sizeof(__pyx_k__red_lut), 0, 0, 1, 1}, {&__pyx_n_s__reshape, __pyx_k__reshape, sizeof(__pyx_k__reshape), 0, 0, 1, 1}, {&__pyx_n_s__rgba, __pyx_k__rgba, sizeof(__pyx_k__rgba), 0, 0, 1, 1}, {&__pyx_n_s__shape, __pyx_k__shape, sizeof(__pyx_k__shape), 0, 0, 1, 1}, {&__pyx_n_s__size, __pyx_k__size, sizeof(__pyx_k__size), 0, 0, 1, 1}, {&__pyx_n_s__steps, __pyx_k__steps, sizeof(__pyx_k__steps), 0, 0, 1, 1}, {&__pyx_n_s__sys, __pyx_k__sys, sizeof(__pyx_k__sys), 0, 0, 1, 1}, {&__pyx_n_s__uint8, __pyx_k__uint8, sizeof(__pyx_k__uint8), 0, 0, 1, 1}, {&__pyx_n_s__view, __pyx_k__view, sizeof(__pyx_k__view), 0, 0, 1, 1}, {0, 0, 0, 0, 0, 0, 0} }; static int __Pyx_InitCachedBuiltins(void) { __pyx_builtin_range = __Pyx_GetName(__pyx_b, __pyx_n_s__range); if (!__pyx_builtin_range) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 81; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __pyx_builtin_ValueError = __Pyx_GetName(__pyx_b, __pyx_n_s__ValueError); if (!__pyx_builtin_ValueError) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 214; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __pyx_builtin_RuntimeError = __Pyx_GetName(__pyx_b, __pyx_n_s__RuntimeError); if (!__pyx_builtin_RuntimeError) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 798; __pyx_clineno = __LINE__; goto __pyx_L1_error;} return 0; __pyx_L1_error:; return -1; } static int __Pyx_InitCachedConstants(void) { __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("__Pyx_InitCachedConstants", 0); /* "chaco/_cython_speedups.pyx":65 * * ''' * shape = data_array.shape + (4,) # <<<<<<<<<<<<<< * cdef int i, idx, N = data_array.size * cdef float norm_value, range_diff, red, green, blue, alpha */ __pyx_k_tuple_1 = PyTuple_New(1); if (unlikely(!__pyx_k_tuple_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 65; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_k_tuple_1); __Pyx_INCREF(__pyx_int_4); PyTuple_SET_ITEM(__pyx_k_tuple_1, 0, __pyx_int_4); __Pyx_GIVEREF(__pyx_int_4); __Pyx_GIVEREF(((PyObject *)__pyx_k_tuple_1)); /* "chaco/_cython_speedups.pyx":90 * # Copy the data into a float32 array so we can use fast iteration. We use * # part of the rgba array for this to save memory. * cdef np.ndarray[np.float32_t] norm_data = rgba[:,0] # <<<<<<<<<<<<<< * norm_data[:] = data_array.flat * */ __pyx_k_slice_2 = PySlice_New(Py_None, Py_None, Py_None); if (unlikely(!__pyx_k_slice_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 90; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_k_slice_2); __Pyx_GIVEREF(__pyx_k_slice_2); __pyx_k_tuple_3 = PyTuple_New(2); if (unlikely(!__pyx_k_tuple_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 90; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_k_tuple_3); __Pyx_INCREF(__pyx_k_slice_2); PyTuple_SET_ITEM(__pyx_k_tuple_3, 0, __pyx_k_slice_2); __Pyx_GIVEREF(__pyx_k_slice_2); __Pyx_INCREF(__pyx_int_0); PyTuple_SET_ITEM(__pyx_k_tuple_3, 1, __pyx_int_0); __Pyx_GIVEREF(__pyx_int_0); __Pyx_GIVEREF(((PyObject *)__pyx_k_tuple_3)); /* "chaco/_cython_speedups.pyx":156 * * ''' * shape = data_array.shape + (4,) # <<<<<<<<<<<<<< * cdef int i, idx, N = data_array.size * cdef float norm_value, range_diff */ __pyx_k_tuple_4 = PyTuple_New(1); if (unlikely(!__pyx_k_tuple_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 156; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_k_tuple_4); __Pyx_INCREF(__pyx_int_4); PyTuple_SET_ITEM(__pyx_k_tuple_4, 0, __pyx_int_4); __Pyx_GIVEREF(__pyx_int_4); __Pyx_GIVEREF(((PyObject *)__pyx_k_tuple_4)); /* "numpy.pxd":214 * if ((flags & pybuf.PyBUF_C_CONTIGUOUS == pybuf.PyBUF_C_CONTIGUOUS) * and not PyArray_CHKFLAGS(self, NPY_C_CONTIGUOUS)): * raise ValueError(u"ndarray is not C contiguous") # <<<<<<<<<<<<<< * * if ((flags & pybuf.PyBUF_F_CONTIGUOUS == pybuf.PyBUF_F_CONTIGUOUS) */ __pyx_k_tuple_6 = PyTuple_New(1); if (unlikely(!__pyx_k_tuple_6)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 214; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_k_tuple_6); __Pyx_INCREF(((PyObject *)__pyx_kp_u_5)); PyTuple_SET_ITEM(__pyx_k_tuple_6, 0, ((PyObject *)__pyx_kp_u_5)); __Pyx_GIVEREF(((PyObject *)__pyx_kp_u_5)); __Pyx_GIVEREF(((PyObject *)__pyx_k_tuple_6)); /* "numpy.pxd":218 * if ((flags & pybuf.PyBUF_F_CONTIGUOUS == pybuf.PyBUF_F_CONTIGUOUS) * and not PyArray_CHKFLAGS(self, NPY_F_CONTIGUOUS)): * raise ValueError(u"ndarray is not Fortran contiguous") # <<<<<<<<<<<<<< * * info.buf = PyArray_DATA(self) */ __pyx_k_tuple_8 = PyTuple_New(1); if (unlikely(!__pyx_k_tuple_8)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 218; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_k_tuple_8); __Pyx_INCREF(((PyObject *)__pyx_kp_u_7)); PyTuple_SET_ITEM(__pyx_k_tuple_8, 0, ((PyObject *)__pyx_kp_u_7)); __Pyx_GIVEREF(((PyObject *)__pyx_kp_u_7)); __Pyx_GIVEREF(((PyObject *)__pyx_k_tuple_8)); /* "numpy.pxd":256 * if ((descr.byteorder == '>' and little_endian) or * (descr.byteorder == '<' and not little_endian)): * raise ValueError(u"Non-native byte order not supported") # <<<<<<<<<<<<<< * if t == NPY_BYTE: f = "b" * elif t == NPY_UBYTE: f = "B" */ __pyx_k_tuple_10 = PyTuple_New(1); if (unlikely(!__pyx_k_tuple_10)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 256; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_k_tuple_10); __Pyx_INCREF(((PyObject *)__pyx_kp_u_9)); PyTuple_SET_ITEM(__pyx_k_tuple_10, 0, ((PyObject *)__pyx_kp_u_9)); __Pyx_GIVEREF(((PyObject *)__pyx_kp_u_9)); __Pyx_GIVEREF(((PyObject *)__pyx_k_tuple_10)); /* "numpy.pxd":798 * * if (end - f) - (new_offset - offset[0]) < 15: * raise RuntimeError(u"Format string allocated too short, see comment in numpy.pxd") # <<<<<<<<<<<<<< * * if ((child.byteorder == '>' and little_endian) or */ __pyx_k_tuple_13 = PyTuple_New(1); if (unlikely(!__pyx_k_tuple_13)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 798; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_k_tuple_13); __Pyx_INCREF(((PyObject *)__pyx_kp_u_12)); PyTuple_SET_ITEM(__pyx_k_tuple_13, 0, ((PyObject *)__pyx_kp_u_12)); __Pyx_GIVEREF(((PyObject *)__pyx_kp_u_12)); __Pyx_GIVEREF(((PyObject *)__pyx_k_tuple_13)); /* "numpy.pxd":802 * if ((child.byteorder == '>' and little_endian) or * (child.byteorder == '<' and not little_endian)): * raise ValueError(u"Non-native byte order not supported") # <<<<<<<<<<<<<< * # One could encode it in the format string and have Cython * # complain instead, BUT: < and > in format strings also imply */ __pyx_k_tuple_14 = PyTuple_New(1); if (unlikely(!__pyx_k_tuple_14)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 802; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_k_tuple_14); __Pyx_INCREF(((PyObject *)__pyx_kp_u_9)); PyTuple_SET_ITEM(__pyx_k_tuple_14, 0, ((PyObject *)__pyx_kp_u_9)); __Pyx_GIVEREF(((PyObject *)__pyx_kp_u_9)); __Pyx_GIVEREF(((PyObject *)__pyx_k_tuple_14)); /* "numpy.pxd":822 * t = child.type_num * if end - f < 5: * raise RuntimeError(u"Format string allocated too short.") # <<<<<<<<<<<<<< * * # Until ticket #99 is fixed, use integers to avoid warnings */ __pyx_k_tuple_16 = PyTuple_New(1); if (unlikely(!__pyx_k_tuple_16)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 822; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_k_tuple_16); __Pyx_INCREF(((PyObject *)__pyx_kp_u_15)); PyTuple_SET_ITEM(__pyx_k_tuple_16, 0, ((PyObject *)__pyx_kp_u_15)); __Pyx_GIVEREF(((PyObject *)__pyx_kp_u_15)); __Pyx_GIVEREF(((PyObject *)__pyx_k_tuple_16)); /* "chaco/_cython_speedups.pyx":25 * @cython.boundscheck(False) * @cython.cdivision(True) * def map_colors( # <<<<<<<<<<<<<< * data_array not None, * int steps, */ __pyx_k_tuple_17 = PyTuple_New(20); if (unlikely(!__pyx_k_tuple_17)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 25; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_k_tuple_17); __Pyx_INCREF(((PyObject *)__pyx_n_s__data_array)); PyTuple_SET_ITEM(__pyx_k_tuple_17, 0, ((PyObject *)__pyx_n_s__data_array)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__data_array)); __Pyx_INCREF(((PyObject *)__pyx_n_s__steps)); PyTuple_SET_ITEM(__pyx_k_tuple_17, 1, ((PyObject *)__pyx_n_s__steps)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__steps)); __Pyx_INCREF(((PyObject *)__pyx_n_s__low)); PyTuple_SET_ITEM(__pyx_k_tuple_17, 2, ((PyObject *)__pyx_n_s__low)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__low)); __Pyx_INCREF(((PyObject *)__pyx_n_s__high)); PyTuple_SET_ITEM(__pyx_k_tuple_17, 3, ((PyObject *)__pyx_n_s__high)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__high)); __Pyx_INCREF(((PyObject *)__pyx_n_s__red_lut)); PyTuple_SET_ITEM(__pyx_k_tuple_17, 4, ((PyObject *)__pyx_n_s__red_lut)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__red_lut)); __Pyx_INCREF(((PyObject *)__pyx_n_s__green_lut)); PyTuple_SET_ITEM(__pyx_k_tuple_17, 5, ((PyObject *)__pyx_n_s__green_lut)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__green_lut)); __Pyx_INCREF(((PyObject *)__pyx_n_s__blue_lut)); PyTuple_SET_ITEM(__pyx_k_tuple_17, 6, ((PyObject *)__pyx_n_s__blue_lut)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__blue_lut)); __Pyx_INCREF(((PyObject *)__pyx_n_s__alpha_lut)); PyTuple_SET_ITEM(__pyx_k_tuple_17, 7, ((PyObject *)__pyx_n_s__alpha_lut)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__alpha_lut)); __Pyx_INCREF(((PyObject *)__pyx_n_s__shape)); PyTuple_SET_ITEM(__pyx_k_tuple_17, 8, ((PyObject *)__pyx_n_s__shape)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__shape)); __Pyx_INCREF(((PyObject *)__pyx_n_s__i)); PyTuple_SET_ITEM(__pyx_k_tuple_17, 9, ((PyObject *)__pyx_n_s__i)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__i)); __Pyx_INCREF(((PyObject *)__pyx_n_s__idx)); PyTuple_SET_ITEM(__pyx_k_tuple_17, 10, ((PyObject *)__pyx_n_s__idx)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__idx)); __Pyx_INCREF(((PyObject *)__pyx_n_s__N)); PyTuple_SET_ITEM(__pyx_k_tuple_17, 11, ((PyObject *)__pyx_n_s__N)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__N)); __Pyx_INCREF(((PyObject *)__pyx_n_s__norm_value)); PyTuple_SET_ITEM(__pyx_k_tuple_17, 12, ((PyObject *)__pyx_n_s__norm_value)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__norm_value)); __Pyx_INCREF(((PyObject *)__pyx_n_s__range_diff)); PyTuple_SET_ITEM(__pyx_k_tuple_17, 13, ((PyObject *)__pyx_n_s__range_diff)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__range_diff)); __Pyx_INCREF(((PyObject *)__pyx_n_s__red)); PyTuple_SET_ITEM(__pyx_k_tuple_17, 14, ((PyObject *)__pyx_n_s__red)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__red)); __Pyx_INCREF(((PyObject *)__pyx_n_s__green)); PyTuple_SET_ITEM(__pyx_k_tuple_17, 15, ((PyObject *)__pyx_n_s__green)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__green)); __Pyx_INCREF(((PyObject *)__pyx_n_s__blue)); PyTuple_SET_ITEM(__pyx_k_tuple_17, 16, ((PyObject *)__pyx_n_s__blue)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__blue)); __Pyx_INCREF(((PyObject *)__pyx_n_s__alpha)); PyTuple_SET_ITEM(__pyx_k_tuple_17, 17, ((PyObject *)__pyx_n_s__alpha)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__alpha)); __Pyx_INCREF(((PyObject *)__pyx_n_s__rgba)); PyTuple_SET_ITEM(__pyx_k_tuple_17, 18, ((PyObject *)__pyx_n_s__rgba)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__rgba)); __Pyx_INCREF(((PyObject *)__pyx_n_s__norm_data)); PyTuple_SET_ITEM(__pyx_k_tuple_17, 19, ((PyObject *)__pyx_n_s__norm_data)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__norm_data)); __Pyx_GIVEREF(((PyObject *)__pyx_k_tuple_17)); __pyx_k_codeobj_18 = (PyObject*)__Pyx_PyCode_New(8, 0, 20, 0, 0, __pyx_empty_bytes, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_k_tuple_17, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_kp_s_19, __pyx_n_s__map_colors, 25, __pyx_empty_bytes); if (unlikely(!__pyx_k_codeobj_18)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 25; __pyx_clineno = __LINE__; goto __pyx_L1_error;} /* "chaco/_cython_speedups.pyx":118 * @cython.boundscheck(False) * @cython.cdivision(True) * def map_colors_uint8(data_array not None, # <<<<<<<<<<<<<< * int steps, * float low, */ __pyx_k_tuple_21 = PyTuple_New(20); if (unlikely(!__pyx_k_tuple_21)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 118; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_k_tuple_21); __Pyx_INCREF(((PyObject *)__pyx_n_s__data_array)); PyTuple_SET_ITEM(__pyx_k_tuple_21, 0, ((PyObject *)__pyx_n_s__data_array)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__data_array)); __Pyx_INCREF(((PyObject *)__pyx_n_s__steps)); PyTuple_SET_ITEM(__pyx_k_tuple_21, 1, ((PyObject *)__pyx_n_s__steps)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__steps)); __Pyx_INCREF(((PyObject *)__pyx_n_s__low)); PyTuple_SET_ITEM(__pyx_k_tuple_21, 2, ((PyObject *)__pyx_n_s__low)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__low)); __Pyx_INCREF(((PyObject *)__pyx_n_s__high)); PyTuple_SET_ITEM(__pyx_k_tuple_21, 3, ((PyObject *)__pyx_n_s__high)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__high)); __Pyx_INCREF(((PyObject *)__pyx_n_s__red_lut)); PyTuple_SET_ITEM(__pyx_k_tuple_21, 4, ((PyObject *)__pyx_n_s__red_lut)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__red_lut)); __Pyx_INCREF(((PyObject *)__pyx_n_s__green_lut)); PyTuple_SET_ITEM(__pyx_k_tuple_21, 5, ((PyObject *)__pyx_n_s__green_lut)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__green_lut)); __Pyx_INCREF(((PyObject *)__pyx_n_s__blue_lut)); PyTuple_SET_ITEM(__pyx_k_tuple_21, 6, ((PyObject *)__pyx_n_s__blue_lut)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__blue_lut)); __Pyx_INCREF(((PyObject *)__pyx_n_s__alpha_lut)); PyTuple_SET_ITEM(__pyx_k_tuple_21, 7, ((PyObject *)__pyx_n_s__alpha_lut)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__alpha_lut)); __Pyx_INCREF(((PyObject *)__pyx_n_s__shape)); PyTuple_SET_ITEM(__pyx_k_tuple_21, 8, ((PyObject *)__pyx_n_s__shape)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__shape)); __Pyx_INCREF(((PyObject *)__pyx_n_s__i)); PyTuple_SET_ITEM(__pyx_k_tuple_21, 9, ((PyObject *)__pyx_n_s__i)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__i)); __Pyx_INCREF(((PyObject *)__pyx_n_s__idx)); PyTuple_SET_ITEM(__pyx_k_tuple_21, 10, ((PyObject *)__pyx_n_s__idx)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__idx)); __Pyx_INCREF(((PyObject *)__pyx_n_s__N)); PyTuple_SET_ITEM(__pyx_k_tuple_21, 11, ((PyObject *)__pyx_n_s__N)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__N)); __Pyx_INCREF(((PyObject *)__pyx_n_s__norm_value)); PyTuple_SET_ITEM(__pyx_k_tuple_21, 12, ((PyObject *)__pyx_n_s__norm_value)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__norm_value)); __Pyx_INCREF(((PyObject *)__pyx_n_s__range_diff)); PyTuple_SET_ITEM(__pyx_k_tuple_21, 13, ((PyObject *)__pyx_n_s__range_diff)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__range_diff)); __Pyx_INCREF(((PyObject *)__pyx_n_s__red)); PyTuple_SET_ITEM(__pyx_k_tuple_21, 14, ((PyObject *)__pyx_n_s__red)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__red)); __Pyx_INCREF(((PyObject *)__pyx_n_s__green)); PyTuple_SET_ITEM(__pyx_k_tuple_21, 15, ((PyObject *)__pyx_n_s__green)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__green)); __Pyx_INCREF(((PyObject *)__pyx_n_s__blue)); PyTuple_SET_ITEM(__pyx_k_tuple_21, 16, ((PyObject *)__pyx_n_s__blue)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__blue)); __Pyx_INCREF(((PyObject *)__pyx_n_s__alpha)); PyTuple_SET_ITEM(__pyx_k_tuple_21, 17, ((PyObject *)__pyx_n_s__alpha)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__alpha)); __Pyx_INCREF(((PyObject *)__pyx_n_s__rgba)); PyTuple_SET_ITEM(__pyx_k_tuple_21, 18, ((PyObject *)__pyx_n_s__rgba)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__rgba)); __Pyx_INCREF(((PyObject *)__pyx_n_s__norm_data)); PyTuple_SET_ITEM(__pyx_k_tuple_21, 19, ((PyObject *)__pyx_n_s__norm_data)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__norm_data)); __Pyx_GIVEREF(((PyObject *)__pyx_k_tuple_21)); __pyx_k_codeobj_22 = (PyObject*)__Pyx_PyCode_New(8, 0, 20, 0, 0, __pyx_empty_bytes, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_k_tuple_21, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_kp_s_19, __pyx_n_s__map_colors_uint8, 118, __pyx_empty_bytes); if (unlikely(!__pyx_k_codeobj_22)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 118; __pyx_clineno = __LINE__; goto __pyx_L1_error;} /* "chaco/_cython_speedups.pyx":210 * @cython.wraparound(False) * @cython.boundscheck(False) * def apply_selection_fade( # <<<<<<<<<<<<<< * np.ndarray[np.uint8_t, ndim=3] mapped_image not None, * mask, fade_alpha, fade_background): */ __pyx_k_tuple_23 = PyTuple_New(20); if (unlikely(!__pyx_k_tuple_23)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 210; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_k_tuple_23); __Pyx_INCREF(((PyObject *)__pyx_n_s__mapped_image)); PyTuple_SET_ITEM(__pyx_k_tuple_23, 0, ((PyObject *)__pyx_n_s__mapped_image)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__mapped_image)); __Pyx_INCREF(((PyObject *)__pyx_n_s__mask)); PyTuple_SET_ITEM(__pyx_k_tuple_23, 1, ((PyObject *)__pyx_n_s__mask)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__mask)); __Pyx_INCREF(((PyObject *)__pyx_n_s__fade_alpha)); PyTuple_SET_ITEM(__pyx_k_tuple_23, 2, ((PyObject *)__pyx_n_s__fade_alpha)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__fade_alpha)); __Pyx_INCREF(((PyObject *)__pyx_n_s__fade_background)); PyTuple_SET_ITEM(__pyx_k_tuple_23, 3, ((PyObject *)__pyx_n_s__fade_background)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__fade_background)); __Pyx_INCREF(((PyObject *)__pyx_n_s__ialpha)); PyTuple_SET_ITEM(__pyx_k_tuple_23, 4, ((PyObject *)__pyx_n_s__ialpha)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__ialpha)); __Pyx_INCREF(((PyObject *)__pyx_n_s__bg_r)); PyTuple_SET_ITEM(__pyx_k_tuple_23, 5, ((PyObject *)__pyx_n_s__bg_r)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__bg_r)); __Pyx_INCREF(((PyObject *)__pyx_n_s__bg_g)); PyTuple_SET_ITEM(__pyx_k_tuple_23, 6, ((PyObject *)__pyx_n_s__bg_g)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__bg_g)); __Pyx_INCREF(((PyObject *)__pyx_n_s__bg_b)); PyTuple_SET_ITEM(__pyx_k_tuple_23, 7, ((PyObject *)__pyx_n_s__bg_b)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__bg_b)); __Pyx_INCREF(((PyObject *)__pyx_n_s__N)); PyTuple_SET_ITEM(__pyx_k_tuple_23, 8, ((PyObject *)__pyx_n_s__N)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__N)); __Pyx_INCREF(((PyObject *)__pyx_n_s__M)); PyTuple_SET_ITEM(__pyx_k_tuple_23, 9, ((PyObject *)__pyx_n_s__M)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__M)); __Pyx_INCREF(((PyObject *)__pyx_n_s__i)); PyTuple_SET_ITEM(__pyx_k_tuple_23, 10, ((PyObject *)__pyx_n_s__i)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__i)); __Pyx_INCREF(((PyObject *)__pyx_n_s__j)); PyTuple_SET_ITEM(__pyx_k_tuple_23, 11, ((PyObject *)__pyx_n_s__j)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__j)); __Pyx_INCREF(((PyObject *)__pyx_n_s__red_lut)); PyTuple_SET_ITEM(__pyx_k_tuple_23, 12, ((PyObject *)__pyx_n_s__red_lut)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__red_lut)); __Pyx_INCREF(((PyObject *)__pyx_n_s__green_lut)); PyTuple_SET_ITEM(__pyx_k_tuple_23, 13, ((PyObject *)__pyx_n_s__green_lut)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__green_lut)); __Pyx_INCREF(((PyObject *)__pyx_n_s__blue_lut)); PyTuple_SET_ITEM(__pyx_k_tuple_23, 14, ((PyObject *)__pyx_n_s__blue_lut)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__blue_lut)); __Pyx_INCREF(((PyObject *)__pyx_n_s__red)); PyTuple_SET_ITEM(__pyx_k_tuple_23, 15, ((PyObject *)__pyx_n_s__red)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__red)); __Pyx_INCREF(((PyObject *)__pyx_n_s__green)); PyTuple_SET_ITEM(__pyx_k_tuple_23, 16, ((PyObject *)__pyx_n_s__green)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__green)); __Pyx_INCREF(((PyObject *)__pyx_n_s__blue)); PyTuple_SET_ITEM(__pyx_k_tuple_23, 17, ((PyObject *)__pyx_n_s__blue)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__blue)); __Pyx_INCREF(((PyObject *)__pyx_n_s__fade)); PyTuple_SET_ITEM(__pyx_k_tuple_23, 18, ((PyObject *)__pyx_n_s__fade)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__fade)); __Pyx_INCREF(((PyObject *)__pyx_n_s__i8mask)); PyTuple_SET_ITEM(__pyx_k_tuple_23, 19, ((PyObject *)__pyx_n_s__i8mask)); __Pyx_GIVEREF(((PyObject *)__pyx_n_s__i8mask)); __Pyx_GIVEREF(((PyObject *)__pyx_k_tuple_23)); __pyx_k_codeobj_24 = (PyObject*)__Pyx_PyCode_New(4, 0, 20, 0, 0, __pyx_empty_bytes, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_k_tuple_23, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_kp_s_19, __pyx_n_s_25, 210, __pyx_empty_bytes); if (unlikely(!__pyx_k_codeobj_24)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 210; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_RefNannyFinishContext(); return 0; __pyx_L1_error:; __Pyx_RefNannyFinishContext(); return -1; } static int __Pyx_InitGlobals(void) { if (__Pyx_InitStrings(__pyx_string_tab) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; __pyx_int_0 = PyInt_FromLong(0); if (unlikely(!__pyx_int_0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; __pyx_int_4 = PyInt_FromLong(4); if (unlikely(!__pyx_int_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; __pyx_int_15 = PyInt_FromLong(15); if (unlikely(!__pyx_int_15)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; __pyx_int_256 = PyInt_FromLong(256); if (unlikely(!__pyx_int_256)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; return 0; __pyx_L1_error:; return -1; } #if PY_MAJOR_VERSION < 3 PyMODINIT_FUNC init_cython_speedups(void); /*proto*/ PyMODINIT_FUNC init_cython_speedups(void) #else PyMODINIT_FUNC PyInit__cython_speedups(void); /*proto*/ PyMODINIT_FUNC PyInit__cython_speedups(void) #endif { PyObject *__pyx_t_1 = NULL; __Pyx_RefNannyDeclarations #if CYTHON_REFNANNY __Pyx_RefNanny = __Pyx_RefNannyImportAPI("refnanny"); if (!__Pyx_RefNanny) { PyErr_Clear(); __Pyx_RefNanny = __Pyx_RefNannyImportAPI("Cython.Runtime.refnanny"); if (!__Pyx_RefNanny) Py_FatalError("failed to import 'refnanny' module"); } #endif __Pyx_RefNannySetupContext("PyMODINIT_FUNC PyInit__cython_speedups(void)", 0); if ( __Pyx_check_binary_version() < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __pyx_empty_tuple = PyTuple_New(0); if (unlikely(!__pyx_empty_tuple)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __pyx_empty_bytes = PyBytes_FromStringAndSize("", 0); if (unlikely(!__pyx_empty_bytes)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} #ifdef __Pyx_CyFunction_USED if (__Pyx_CyFunction_init() < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} #endif #ifdef __Pyx_FusedFunction_USED if (__pyx_FusedFunction_init() < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} #endif #ifdef __Pyx_Generator_USED if (__pyx_Generator_init() < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} #endif /*--- Library function declarations ---*/ /*--- Threads initialization code ---*/ #if defined(__PYX_FORCE_INIT_THREADS) && __PYX_FORCE_INIT_THREADS #ifdef WITH_THREAD /* Python build with threading support? */ PyEval_InitThreads(); #endif #endif /*--- Module creation code ---*/ #if PY_MAJOR_VERSION < 3 __pyx_m = Py_InitModule4(__Pyx_NAMESTR("_cython_speedups"), __pyx_methods, 0, 0, PYTHON_API_VERSION); #else __pyx_m = PyModule_Create(&__pyx_moduledef); #endif if (!__pyx_m) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; #if PY_MAJOR_VERSION < 3 Py_INCREF(__pyx_m); #endif __pyx_b = PyImport_AddModule(__Pyx_NAMESTR(__Pyx_BUILTIN_MODULE_NAME)); if (!__pyx_b) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; if (__Pyx_SetAttrString(__pyx_m, "__builtins__", __pyx_b) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; /*--- Initialize various global constants etc. ---*/ if (unlikely(__Pyx_InitGlobals() < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} if (__pyx_module_is_main_chaco___cython_speedups) { if (__Pyx_SetAttrString(__pyx_m, "__name__", __pyx_n_s____main__) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; } /*--- Builtin init code ---*/ if (unlikely(__Pyx_InitCachedBuiltins() < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} /*--- Constants init code ---*/ if (unlikely(__Pyx_InitCachedConstants() < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} /*--- Global init code ---*/ /*--- Variable export code ---*/ /*--- Function export code ---*/ /*--- Type init code ---*/ /*--- Type import code ---*/ __pyx_ptype_5numpy_dtype = __Pyx_ImportType("numpy", "dtype", sizeof(PyArray_Descr), 0); if (unlikely(!__pyx_ptype_5numpy_dtype)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 154; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __pyx_ptype_5numpy_flatiter = __Pyx_ImportType("numpy", "flatiter", sizeof(PyArrayIterObject), 0); if (unlikely(!__pyx_ptype_5numpy_flatiter)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 164; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __pyx_ptype_5numpy_broadcast = __Pyx_ImportType("numpy", "broadcast", sizeof(PyArrayMultiIterObject), 0); if (unlikely(!__pyx_ptype_5numpy_broadcast)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 168; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __pyx_ptype_5numpy_ndarray = __Pyx_ImportType("numpy", "ndarray", sizeof(PyArrayObject), 0); if (unlikely(!__pyx_ptype_5numpy_ndarray)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 177; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __pyx_ptype_5numpy_ufunc = __Pyx_ImportType("numpy", "ufunc", sizeof(PyUFuncObject), 0); if (unlikely(!__pyx_ptype_5numpy_ufunc)) {__pyx_filename = __pyx_f[1]; __pyx_lineno = 860; __pyx_clineno = __LINE__; goto __pyx_L1_error;} /*--- Variable import code ---*/ /*--- Function import code ---*/ /*--- Execution code ---*/ /* "chaco/_cython_speedups.pyx":1 * import sys # <<<<<<<<<<<<<< * import numpy as np * cimport numpy as np */ __pyx_t_1 = __Pyx_Import(((PyObject *)__pyx_n_s__sys), 0, -1); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_1); if (PyObject_SetAttr(__pyx_m, __pyx_n_s__sys, __pyx_t_1) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; /* "chaco/_cython_speedups.pyx":2 * import sys * import numpy as np # <<<<<<<<<<<<<< * cimport numpy as np * */ __pyx_t_1 = __Pyx_Import(((PyObject *)__pyx_n_s__numpy), 0, -1); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 2; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_1); if (PyObject_SetAttr(__pyx_m, __pyx_n_s__np, __pyx_t_1) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 2; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; /* "chaco/_cython_speedups.pyx":25 * @cython.boundscheck(False) * @cython.cdivision(True) * def map_colors( # <<<<<<<<<<<<<< * data_array not None, * int steps, */ __pyx_t_1 = PyCFunction_NewEx(&__pyx_mdef_5chaco_16_cython_speedups_1map_colors, NULL, __pyx_n_s_20); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 25; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_1); if (PyObject_SetAttr(__pyx_m, __pyx_n_s__map_colors, __pyx_t_1) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 25; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; /* "chaco/_cython_speedups.pyx":118 * @cython.boundscheck(False) * @cython.cdivision(True) * def map_colors_uint8(data_array not None, # <<<<<<<<<<<<<< * int steps, * float low, */ __pyx_t_1 = PyCFunction_NewEx(&__pyx_mdef_5chaco_16_cython_speedups_3map_colors_uint8, NULL, __pyx_n_s_20); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 118; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_1); if (PyObject_SetAttr(__pyx_m, __pyx_n_s__map_colors_uint8, __pyx_t_1) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 118; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; /* "chaco/_cython_speedups.pyx":210 * @cython.wraparound(False) * @cython.boundscheck(False) * def apply_selection_fade( # <<<<<<<<<<<<<< * np.ndarray[np.uint8_t, ndim=3] mapped_image not None, * mask, fade_alpha, fade_background): */ __pyx_t_1 = PyCFunction_NewEx(&__pyx_mdef_5chaco_16_cython_speedups_5apply_selection_fade, NULL, __pyx_n_s_20); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 210; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_1); if (PyObject_SetAttr(__pyx_m, __pyx_n_s_25, __pyx_t_1) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 210; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; /* "chaco/_cython_speedups.pyx":1 * import sys # <<<<<<<<<<<<<< * import numpy as np * cimport numpy as np */ __pyx_t_1 = PyDict_New(); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(((PyObject *)__pyx_t_1)); if (PyObject_SetAttr(__pyx_m, __pyx_n_s____test__, ((PyObject *)__pyx_t_1)) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(((PyObject *)__pyx_t_1)); __pyx_t_1 = 0; /* "numpy.pxd":974 * arr.base = baseptr * * cdef inline object get_array_base(ndarray arr): # <<<<<<<<<<<<<< * if arr.base is NULL: * return None */ goto __pyx_L0; __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_1); if (__pyx_m) { __Pyx_AddTraceback("init chaco._cython_speedups", __pyx_clineno, __pyx_lineno, __pyx_filename); Py_DECREF(__pyx_m); __pyx_m = 0; } else if (!PyErr_Occurred()) { PyErr_SetString(PyExc_ImportError, "init chaco._cython_speedups"); } __pyx_L0:; __Pyx_RefNannyFinishContext(); #if PY_MAJOR_VERSION < 3 return; #else return __pyx_m; #endif } /* Runtime support code */ #if CYTHON_REFNANNY static __Pyx_RefNannyAPIStruct *__Pyx_RefNannyImportAPI(const char *modname) { PyObject *m = NULL, *p = NULL; void *r = NULL; m = PyImport_ImportModule((char *)modname); if (!m) goto end; p = PyObject_GetAttrString(m, (char *)"RefNannyAPI"); if (!p) goto end; r = PyLong_AsVoidPtr(p); end: Py_XDECREF(p); Py_XDECREF(m); return (__Pyx_RefNannyAPIStruct *)r; } #endif /* CYTHON_REFNANNY */ static PyObject *__Pyx_GetName(PyObject *dict, PyObject *name) { PyObject *result; result = PyObject_GetAttr(dict, name); if (!result) { if (dict != __pyx_b) { PyErr_Clear(); result = PyObject_GetAttr(__pyx_b, name); } if (!result) { PyErr_SetObject(PyExc_NameError, name); } } return result; } static void __Pyx_RaiseArgtupleInvalid( const char* func_name, int exact, Py_ssize_t num_min, Py_ssize_t num_max, Py_ssize_t num_found) { Py_ssize_t num_expected; const char *more_or_less; if (num_found < num_min) { num_expected = num_min; more_or_less = "at least"; } else { num_expected = num_max; more_or_less = "at most"; } if (exact) { more_or_less = "exactly"; } PyErr_Format(PyExc_TypeError, "%s() takes %s %"PY_FORMAT_SIZE_T"d positional argument%s (%"PY_FORMAT_SIZE_T"d given)", func_name, more_or_less, num_expected, (num_expected == 1) ? "" : "s", num_found); } static void __Pyx_RaiseDoubleKeywordsError( const char* func_name, PyObject* kw_name) { PyErr_Format(PyExc_TypeError, #if PY_MAJOR_VERSION >= 3 "%s() got multiple values for keyword argument '%U'", func_name, kw_name); #else "%s() got multiple values for keyword argument '%s'", func_name, PyString_AS_STRING(kw_name)); #endif } static int __Pyx_ParseOptionalKeywords( PyObject *kwds, PyObject **argnames[], PyObject *kwds2, PyObject *values[], Py_ssize_t num_pos_args, const char* function_name) { PyObject *key = 0, *value = 0; Py_ssize_t pos = 0; PyObject*** name; PyObject*** first_kw_arg = argnames + num_pos_args; while (PyDict_Next(kwds, &pos, &key, &value)) { name = first_kw_arg; while (*name && (**name != key)) name++; if (*name) { values[name-argnames] = value; } else { #if PY_MAJOR_VERSION < 3 if (unlikely(!PyString_CheckExact(key)) && unlikely(!PyString_Check(key))) { #else if (unlikely(!PyUnicode_Check(key))) { #endif goto invalid_keyword_type; } else { for (name = first_kw_arg; *name; name++) { #if PY_MAJOR_VERSION >= 3 if (PyUnicode_GET_SIZE(**name) == PyUnicode_GET_SIZE(key) && PyUnicode_Compare(**name, key) == 0) break; #else if (PyString_GET_SIZE(**name) == PyString_GET_SIZE(key) && _PyString_Eq(**name, key)) break; #endif } if (*name) { values[name-argnames] = value; } else { for (name=argnames; name != first_kw_arg; name++) { if (**name == key) goto arg_passed_twice; #if PY_MAJOR_VERSION >= 3 if (PyUnicode_GET_SIZE(**name) == PyUnicode_GET_SIZE(key) && PyUnicode_Compare(**name, key) == 0) goto arg_passed_twice; #else if (PyString_GET_SIZE(**name) == PyString_GET_SIZE(key) && _PyString_Eq(**name, key)) goto arg_passed_twice; #endif } if (kwds2) { if (unlikely(PyDict_SetItem(kwds2, key, value))) goto bad; } else { goto invalid_keyword; } } } } } return 0; arg_passed_twice: __Pyx_RaiseDoubleKeywordsError(function_name, **name); goto bad; invalid_keyword_type: PyErr_Format(PyExc_TypeError, "%s() keywords must be strings", function_name); goto bad; invalid_keyword: PyErr_Format(PyExc_TypeError, #if PY_MAJOR_VERSION < 3 "%s() got an unexpected keyword argument '%s'", function_name, PyString_AsString(key)); #else "%s() got an unexpected keyword argument '%U'", function_name, key); #endif bad: return -1; } static int __Pyx_ArgTypeTest(PyObject *obj, PyTypeObject *type, int none_allowed, const char *name, int exact) { if (!type) { PyErr_Format(PyExc_SystemError, "Missing type object"); return 0; } if (none_allowed && obj == Py_None) return 1; else if (exact) { if (Py_TYPE(obj) == type) return 1; } else { if (PyObject_TypeCheck(obj, type)) return 1; } PyErr_Format(PyExc_TypeError, "Argument '%s' has incorrect type (expected %s, got %s)", name, type->tp_name, Py_TYPE(obj)->tp_name); return 0; } static CYTHON_INLINE int __Pyx_IsLittleEndian(void) { unsigned int n = 1; return *(unsigned char*)(&n) != 0; } static void __Pyx_BufFmt_Init(__Pyx_BufFmt_Context* ctx, __Pyx_BufFmt_StackElem* stack, __Pyx_TypeInfo* type) { stack[0].field = &ctx->root; stack[0].parent_offset = 0; ctx->root.type = type; ctx->root.name = "buffer dtype"; ctx->root.offset = 0; ctx->head = stack; ctx->head->field = &ctx->root; ctx->fmt_offset = 0; ctx->head->parent_offset = 0; ctx->new_packmode = '@'; ctx->enc_packmode = '@'; ctx->new_count = 1; ctx->enc_count = 0; ctx->enc_type = 0; ctx->is_complex = 0; ctx->is_valid_array = 0; ctx->struct_alignment = 0; while (type->typegroup == 'S') { ++ctx->head; ctx->head->field = type->fields; ctx->head->parent_offset = 0; type = type->fields->type; } } static int __Pyx_BufFmt_ParseNumber(const char** ts) { int count; const char* t = *ts; if (*t < '0' || *t > '9') { return -1; } else { count = *t++ - '0'; while (*t >= '0' && *t < '9') { count *= 10; count += *t++ - '0'; } } *ts = t; return count; } static int __Pyx_BufFmt_ExpectNumber(const char **ts) { int number = __Pyx_BufFmt_ParseNumber(ts); if (number == -1) /* First char was not a digit */ PyErr_Format(PyExc_ValueError,\ "Does not understand character buffer dtype format string ('%c')", **ts); return number; } static void __Pyx_BufFmt_RaiseUnexpectedChar(char ch) { PyErr_Format(PyExc_ValueError, "Unexpected format string character: '%c'", ch); } static const char* __Pyx_BufFmt_DescribeTypeChar(char ch, int is_complex) { switch (ch) { case 'b': return "'char'"; case 'B': return "'unsigned char'"; case 'h': return "'short'"; case 'H': return "'unsigned short'"; case 'i': return "'int'"; case 'I': return "'unsigned int'"; case 'l': return "'long'"; case 'L': return "'unsigned long'"; case 'q': return "'long long'"; case 'Q': return "'unsigned long long'"; case 'f': return (is_complex ? "'complex float'" : "'float'"); case 'd': return (is_complex ? "'complex double'" : "'double'"); case 'g': return (is_complex ? "'complex long double'" : "'long double'"); case 'T': return "a struct"; case 'O': return "Python object"; case 'P': return "a pointer"; case 's': case 'p': return "a string"; case 0: return "end"; default: return "unparseable format string"; } } static size_t __Pyx_BufFmt_TypeCharToStandardSize(char ch, int is_complex) { switch (ch) { case '?': case 'c': case 'b': case 'B': case 's': case 'p': return 1; case 'h': case 'H': return 2; case 'i': case 'I': case 'l': case 'L': return 4; case 'q': case 'Q': return 8; case 'f': return (is_complex ? 8 : 4); case 'd': return (is_complex ? 16 : 8); case 'g': { PyErr_SetString(PyExc_ValueError, "Python does not define a standard format string size for long double ('g').."); return 0; } case 'O': case 'P': return sizeof(void*); default: __Pyx_BufFmt_RaiseUnexpectedChar(ch); return 0; } } static size_t __Pyx_BufFmt_TypeCharToNativeSize(char ch, int is_complex) { switch (ch) { case 'c': case 'b': case 'B': case 's': case 'p': return 1; case 'h': case 'H': return sizeof(short); case 'i': case 'I': return sizeof(int); case 'l': case 'L': return sizeof(long); #ifdef HAVE_LONG_LONG case 'q': case 'Q': return sizeof(PY_LONG_LONG); #endif case 'f': return sizeof(float) * (is_complex ? 2 : 1); case 'd': return sizeof(double) * (is_complex ? 2 : 1); case 'g': return sizeof(long double) * (is_complex ? 2 : 1); case 'O': case 'P': return sizeof(void*); default: { __Pyx_BufFmt_RaiseUnexpectedChar(ch); return 0; } } } typedef struct { char c; short x; } __Pyx_st_short; typedef struct { char c; int x; } __Pyx_st_int; typedef struct { char c; long x; } __Pyx_st_long; typedef struct { char c; float x; } __Pyx_st_float; typedef struct { char c; double x; } __Pyx_st_double; typedef struct { char c; long double x; } __Pyx_st_longdouble; typedef struct { char c; void *x; } __Pyx_st_void_p; #ifdef HAVE_LONG_LONG typedef struct { char c; PY_LONG_LONG x; } __Pyx_st_longlong; #endif static size_t __Pyx_BufFmt_TypeCharToAlignment(char ch, int is_complex) { switch (ch) { case '?': case 'c': case 'b': case 'B': case 's': case 'p': return 1; case 'h': case 'H': return sizeof(__Pyx_st_short) - sizeof(short); case 'i': case 'I': return sizeof(__Pyx_st_int) - sizeof(int); case 'l': case 'L': return sizeof(__Pyx_st_long) - sizeof(long); #ifdef HAVE_LONG_LONG case 'q': case 'Q': return sizeof(__Pyx_st_longlong) - sizeof(PY_LONG_LONG); #endif case 'f': return sizeof(__Pyx_st_float) - sizeof(float); case 'd': return sizeof(__Pyx_st_double) - sizeof(double); case 'g': return sizeof(__Pyx_st_longdouble) - sizeof(long double); case 'P': case 'O': return sizeof(__Pyx_st_void_p) - sizeof(void*); default: __Pyx_BufFmt_RaiseUnexpectedChar(ch); return 0; } } /* These are for computing the padding at the end of the struct to align on the first member of the struct. This will probably the same as above, but we don't have any guarantees. */ typedef struct { short x; char c; } __Pyx_pad_short; typedef struct { int x; char c; } __Pyx_pad_int; typedef struct { long x; char c; } __Pyx_pad_long; typedef struct { float x; char c; } __Pyx_pad_float; typedef struct { double x; char c; } __Pyx_pad_double; typedef struct { long double x; char c; } __Pyx_pad_longdouble; typedef struct { void *x; char c; } __Pyx_pad_void_p; #ifdef HAVE_LONG_LONG typedef struct { PY_LONG_LONG x; char c; } __Pyx_pad_longlong; #endif static size_t __Pyx_BufFmt_TypeCharToPadding(char ch, int is_complex) { switch (ch) { case '?': case 'c': case 'b': case 'B': case 's': case 'p': return 1; case 'h': case 'H': return sizeof(__Pyx_pad_short) - sizeof(short); case 'i': case 'I': return sizeof(__Pyx_pad_int) - sizeof(int); case 'l': case 'L': return sizeof(__Pyx_pad_long) - sizeof(long); #ifdef HAVE_LONG_LONG case 'q': case 'Q': return sizeof(__Pyx_pad_longlong) - sizeof(PY_LONG_LONG); #endif case 'f': return sizeof(__Pyx_pad_float) - sizeof(float); case 'd': return sizeof(__Pyx_pad_double) - sizeof(double); case 'g': return sizeof(__Pyx_pad_longdouble) - sizeof(long double); case 'P': case 'O': return sizeof(__Pyx_pad_void_p) - sizeof(void*); default: __Pyx_BufFmt_RaiseUnexpectedChar(ch); return 0; } } static char __Pyx_BufFmt_TypeCharToGroup(char ch, int is_complex) { switch (ch) { case 'c': case 'b': case 'h': case 'i': case 'l': case 'q': case 's': case 'p': return 'I'; case 'B': case 'H': case 'I': case 'L': case 'Q': return 'U'; case 'f': case 'd': case 'g': return (is_complex ? 'C' : 'R'); case 'O': return 'O'; case 'P': return 'P'; default: { __Pyx_BufFmt_RaiseUnexpectedChar(ch); return 0; } } } static void __Pyx_BufFmt_RaiseExpected(__Pyx_BufFmt_Context* ctx) { if (ctx->head == NULL || ctx->head->field == &ctx->root) { const char* expected; const char* quote; if (ctx->head == NULL) { expected = "end"; quote = ""; } else { expected = ctx->head->field->type->name; quote = "'"; } PyErr_Format(PyExc_ValueError, "Buffer dtype mismatch, expected %s%s%s but got %s", quote, expected, quote, __Pyx_BufFmt_DescribeTypeChar(ctx->enc_type, ctx->is_complex)); } else { __Pyx_StructField* field = ctx->head->field; __Pyx_StructField* parent = (ctx->head - 1)->field; PyErr_Format(PyExc_ValueError, "Buffer dtype mismatch, expected '%s' but got %s in '%s.%s'", field->type->name, __Pyx_BufFmt_DescribeTypeChar(ctx->enc_type, ctx->is_complex), parent->type->name, field->name); } } static int __Pyx_BufFmt_ProcessTypeChunk(__Pyx_BufFmt_Context* ctx) { char group; size_t size, offset, arraysize = 1; if (ctx->enc_type == 0) return 0; if (ctx->head->field->type->arraysize[0]) { int i, ndim = 0; if (ctx->enc_type == 's' || ctx->enc_type == 'p') { ctx->is_valid_array = ctx->head->field->type->ndim == 1; ndim = 1; if (ctx->enc_count != ctx->head->field->type->arraysize[0]) { PyErr_Format(PyExc_ValueError, "Expected a dimension of size %zu, got %zu", ctx->head->field->type->arraysize[0], ctx->enc_count); return -1; } } if (!ctx->is_valid_array) { PyErr_Format(PyExc_ValueError, "Expected %d dimensions, got %d", ctx->head->field->type->ndim, ndim); return -1; } for (i = 0; i < ctx->head->field->type->ndim; i++) { arraysize *= ctx->head->field->type->arraysize[i]; } ctx->is_valid_array = 0; ctx->enc_count = 1; } group = __Pyx_BufFmt_TypeCharToGroup(ctx->enc_type, ctx->is_complex); do { __Pyx_StructField* field = ctx->head->field; __Pyx_TypeInfo* type = field->type; if (ctx->enc_packmode == '@' || ctx->enc_packmode == '^') { size = __Pyx_BufFmt_TypeCharToNativeSize(ctx->enc_type, ctx->is_complex); } else { size = __Pyx_BufFmt_TypeCharToStandardSize(ctx->enc_type, ctx->is_complex); } if (ctx->enc_packmode == '@') { size_t align_at = __Pyx_BufFmt_TypeCharToAlignment(ctx->enc_type, ctx->is_complex); size_t align_mod_offset; if (align_at == 0) return -1; align_mod_offset = ctx->fmt_offset % align_at; if (align_mod_offset > 0) ctx->fmt_offset += align_at - align_mod_offset; if (ctx->struct_alignment == 0) ctx->struct_alignment = __Pyx_BufFmt_TypeCharToPadding(ctx->enc_type, ctx->is_complex); } if (type->size != size || type->typegroup != group) { if (type->typegroup == 'C' && type->fields != NULL) { size_t parent_offset = ctx->head->parent_offset + field->offset; ++ctx->head; ctx->head->field = type->fields; ctx->head->parent_offset = parent_offset; continue; } __Pyx_BufFmt_RaiseExpected(ctx); return -1; } offset = ctx->head->parent_offset + field->offset; if (ctx->fmt_offset != offset) { PyErr_Format(PyExc_ValueError, "Buffer dtype mismatch; next field is at offset %"PY_FORMAT_SIZE_T"d but %"PY_FORMAT_SIZE_T"d expected", (Py_ssize_t)ctx->fmt_offset, (Py_ssize_t)offset); return -1; } ctx->fmt_offset += size; if (arraysize) ctx->fmt_offset += (arraysize - 1) * size; --ctx->enc_count; /* Consume from buffer string */ while (1) { if (field == &ctx->root) { ctx->head = NULL; if (ctx->enc_count != 0) { __Pyx_BufFmt_RaiseExpected(ctx); return -1; } break; /* breaks both loops as ctx->enc_count == 0 */ } ctx->head->field = ++field; if (field->type == NULL) { --ctx->head; field = ctx->head->field; continue; } else if (field->type->typegroup == 'S') { size_t parent_offset = ctx->head->parent_offset + field->offset; if (field->type->fields->type == NULL) continue; /* empty struct */ field = field->type->fields; ++ctx->head; ctx->head->field = field; ctx->head->parent_offset = parent_offset; break; } else { break; } } } while (ctx->enc_count); ctx->enc_type = 0; ctx->is_complex = 0; return 0; } static CYTHON_INLINE PyObject * __pyx_buffmt_parse_array(__Pyx_BufFmt_Context* ctx, const char** tsp) { const char *ts = *tsp; int i = 0, number; int ndim = ctx->head->field->type->ndim; ; ++ts; if (ctx->new_count != 1) { PyErr_SetString(PyExc_ValueError, "Cannot handle repeated arrays in format string"); return NULL; } if (__Pyx_BufFmt_ProcessTypeChunk(ctx) == -1) return NULL; while (*ts && *ts != ')') { if (isspace(*ts)) continue; number = __Pyx_BufFmt_ExpectNumber(&ts); if (number == -1) return NULL; if (i < ndim && (size_t) number != ctx->head->field->type->arraysize[i]) return PyErr_Format(PyExc_ValueError, "Expected a dimension of size %zu, got %d", ctx->head->field->type->arraysize[i], number); if (*ts != ',' && *ts != ')') return PyErr_Format(PyExc_ValueError, "Expected a comma in format string, got '%c'", *ts); if (*ts == ',') ts++; i++; } if (i != ndim) return PyErr_Format(PyExc_ValueError, "Expected %d dimension(s), got %d", ctx->head->field->type->ndim, i); if (!*ts) { PyErr_SetString(PyExc_ValueError, "Unexpected end of format string, expected ')'"); return NULL; } ctx->is_valid_array = 1; ctx->new_count = 1; *tsp = ++ts; return Py_None; } static const char* __Pyx_BufFmt_CheckString(__Pyx_BufFmt_Context* ctx, const char* ts) { int got_Z = 0; while (1) { switch(*ts) { case 0: if (ctx->enc_type != 0 && ctx->head == NULL) { __Pyx_BufFmt_RaiseExpected(ctx); return NULL; } if (__Pyx_BufFmt_ProcessTypeChunk(ctx) == -1) return NULL; if (ctx->head != NULL) { __Pyx_BufFmt_RaiseExpected(ctx); return NULL; } return ts; case ' ': case 10: case 13: ++ts; break; case '<': if (!__Pyx_IsLittleEndian()) { PyErr_SetString(PyExc_ValueError, "Little-endian buffer not supported on big-endian compiler"); return NULL; } ctx->new_packmode = '='; ++ts; break; case '>': case '!': if (__Pyx_IsLittleEndian()) { PyErr_SetString(PyExc_ValueError, "Big-endian buffer not supported on little-endian compiler"); return NULL; } ctx->new_packmode = '='; ++ts; break; case '=': case '@': case '^': ctx->new_packmode = *ts++; break; case 'T': /* substruct */ { const char* ts_after_sub; size_t i, struct_count = ctx->new_count; size_t struct_alignment = ctx->struct_alignment; ctx->new_count = 1; ++ts; if (*ts != '{') { PyErr_SetString(PyExc_ValueError, "Buffer acquisition: Expected '{' after 'T'"); return NULL; } if (__Pyx_BufFmt_ProcessTypeChunk(ctx) == -1) return NULL; ctx->enc_type = 0; /* Erase processed last struct element */ ctx->enc_count = 0; ctx->struct_alignment = 0; ++ts; ts_after_sub = ts; for (i = 0; i != struct_count; ++i) { ts_after_sub = __Pyx_BufFmt_CheckString(ctx, ts); if (!ts_after_sub) return NULL; } ts = ts_after_sub; if (struct_alignment) ctx->struct_alignment = struct_alignment; } break; case '}': /* end of substruct; either repeat or move on */ { size_t alignment = ctx->struct_alignment; ++ts; if (__Pyx_BufFmt_ProcessTypeChunk(ctx) == -1) return NULL; ctx->enc_type = 0; /* Erase processed last struct element */ if (alignment && ctx->fmt_offset % alignment) { ctx->fmt_offset += alignment - (ctx->fmt_offset % alignment); } } return ts; case 'x': if (__Pyx_BufFmt_ProcessTypeChunk(ctx) == -1) return NULL; ctx->fmt_offset += ctx->new_count; ctx->new_count = 1; ctx->enc_count = 0; ctx->enc_type = 0; ctx->enc_packmode = ctx->new_packmode; ++ts; break; case 'Z': got_Z = 1; ++ts; if (*ts != 'f' && *ts != 'd' && *ts != 'g') { __Pyx_BufFmt_RaiseUnexpectedChar('Z'); return NULL; } /* fall through */ case 'c': case 'b': case 'B': case 'h': case 'H': case 'i': case 'I': case 'l': case 'L': case 'q': case 'Q': case 'f': case 'd': case 'g': case 'O': case 's': case 'p': if (ctx->enc_type == *ts && got_Z == ctx->is_complex && ctx->enc_packmode == ctx->new_packmode) { ctx->enc_count += ctx->new_count; } else { if (__Pyx_BufFmt_ProcessTypeChunk(ctx) == -1) return NULL; ctx->enc_count = ctx->new_count; ctx->enc_packmode = ctx->new_packmode; ctx->enc_type = *ts; ctx->is_complex = got_Z; } ++ts; ctx->new_count = 1; got_Z = 0; break; case ':': ++ts; while(*ts != ':') ++ts; ++ts; break; case '(': if (!__pyx_buffmt_parse_array(ctx, &ts)) return NULL; break; default: { int number = __Pyx_BufFmt_ExpectNumber(&ts); if (number == -1) return NULL; ctx->new_count = (size_t)number; } } } } static CYTHON_INLINE void __Pyx_ZeroBuffer(Py_buffer* buf) { buf->buf = NULL; buf->obj = NULL; buf->strides = __Pyx_zeros; buf->shape = __Pyx_zeros; buf->suboffsets = __Pyx_minusones; } static CYTHON_INLINE int __Pyx_GetBufferAndValidate( Py_buffer* buf, PyObject* obj, __Pyx_TypeInfo* dtype, int flags, int nd, int cast, __Pyx_BufFmt_StackElem* stack) { if (obj == Py_None || obj == NULL) { __Pyx_ZeroBuffer(buf); return 0; } buf->buf = NULL; if (__Pyx_GetBuffer(obj, buf, flags) == -1) goto fail; if (buf->ndim != nd) { PyErr_Format(PyExc_ValueError, "Buffer has wrong number of dimensions (expected %d, got %d)", nd, buf->ndim); goto fail; } if (!cast) { __Pyx_BufFmt_Context ctx; __Pyx_BufFmt_Init(&ctx, stack, dtype); if (!__Pyx_BufFmt_CheckString(&ctx, buf->format)) goto fail; } if ((unsigned)buf->itemsize != dtype->size) { PyErr_Format(PyExc_ValueError, "Item size of buffer (%"PY_FORMAT_SIZE_T"d byte%s) does not match size of '%s' (%"PY_FORMAT_SIZE_T"d byte%s)", buf->itemsize, (buf->itemsize > 1) ? "s" : "", dtype->name, (Py_ssize_t)dtype->size, (dtype->size > 1) ? "s" : ""); goto fail; } if (buf->suboffsets == NULL) buf->suboffsets = __Pyx_minusones; return 0; fail:; __Pyx_ZeroBuffer(buf); return -1; } static CYTHON_INLINE void __Pyx_SafeReleaseBuffer(Py_buffer* info) { if (info->buf == NULL) return; if (info->suboffsets == __Pyx_minusones) info->suboffsets = NULL; __Pyx_ReleaseBuffer(info); } static CYTHON_INLINE int __Pyx_TypeTest(PyObject *obj, PyTypeObject *type) { if (unlikely(!type)) { PyErr_Format(PyExc_SystemError, "Missing type object"); return 0; } if (likely(PyObject_TypeCheck(obj, type))) return 1; PyErr_Format(PyExc_TypeError, "Cannot convert %.200s to %.200s", Py_TYPE(obj)->tp_name, type->tp_name); return 0; } static CYTHON_INLINE void __Pyx_ErrRestore(PyObject *type, PyObject *value, PyObject *tb) { #if CYTHON_COMPILING_IN_CPYTHON PyObject *tmp_type, *tmp_value, *tmp_tb; PyThreadState *tstate = PyThreadState_GET(); tmp_type = tstate->curexc_type; tmp_value = tstate->curexc_value; tmp_tb = tstate->curexc_traceback; tstate->curexc_type = type; tstate->curexc_value = value; tstate->curexc_traceback = tb; Py_XDECREF(tmp_type); Py_XDECREF(tmp_value); Py_XDECREF(tmp_tb); #else PyErr_Restore(type, value, tb); #endif } static CYTHON_INLINE void __Pyx_ErrFetch(PyObject **type, PyObject **value, PyObject **tb) { #if CYTHON_COMPILING_IN_CPYTHON PyThreadState *tstate = PyThreadState_GET(); *type = tstate->curexc_type; *value = tstate->curexc_value; *tb = tstate->curexc_traceback; tstate->curexc_type = 0; tstate->curexc_value = 0; tstate->curexc_traceback = 0; #else PyErr_Fetch(type, value, tb); #endif } #if PY_MAJOR_VERSION < 3 static void __Pyx_Raise(PyObject *type, PyObject *value, PyObject *tb, CYTHON_UNUSED PyObject *cause) { Py_XINCREF(type); Py_XINCREF(value); Py_XINCREF(tb); if (tb == Py_None) { Py_DECREF(tb); tb = 0; } else if (tb != NULL && !PyTraceBack_Check(tb)) { PyErr_SetString(PyExc_TypeError, "raise: arg 3 must be a traceback or None"); goto raise_error; } if (value == NULL) { value = Py_None; Py_INCREF(value); } #if PY_VERSION_HEX < 0x02050000 if (!PyClass_Check(type)) #else if (!PyType_Check(type)) #endif { if (value != Py_None) { PyErr_SetString(PyExc_TypeError, "instance exception may not have a separate value"); goto raise_error; } Py_DECREF(value); value = type; #if PY_VERSION_HEX < 0x02050000 if (PyInstance_Check(type)) { type = (PyObject*) ((PyInstanceObject*)type)->in_class; Py_INCREF(type); } else { type = 0; PyErr_SetString(PyExc_TypeError, "raise: exception must be an old-style class or instance"); goto raise_error; } #else type = (PyObject*) Py_TYPE(type); Py_INCREF(type); if (!PyType_IsSubtype((PyTypeObject *)type, (PyTypeObject *)PyExc_BaseException)) { PyErr_SetString(PyExc_TypeError, "raise: exception class must be a subclass of BaseException"); goto raise_error; } #endif } __Pyx_ErrRestore(type, value, tb); return; raise_error: Py_XDECREF(value); Py_XDECREF(type); Py_XDECREF(tb); return; } #else /* Python 3+ */ static void __Pyx_Raise(PyObject *type, PyObject *value, PyObject *tb, PyObject *cause) { if (tb == Py_None) { tb = 0; } else if (tb && !PyTraceBack_Check(tb)) { PyErr_SetString(PyExc_TypeError, "raise: arg 3 must be a traceback or None"); goto bad; } if (value == Py_None) value = 0; if (PyExceptionInstance_Check(type)) { if (value) { PyErr_SetString(PyExc_TypeError, "instance exception may not have a separate value"); goto bad; } value = type; type = (PyObject*) Py_TYPE(value); } else if (!PyExceptionClass_Check(type)) { PyErr_SetString(PyExc_TypeError, "raise: exception class must be a subclass of BaseException"); goto bad; } if (cause) { PyObject *fixed_cause; if (PyExceptionClass_Check(cause)) { fixed_cause = PyObject_CallObject(cause, NULL); if (fixed_cause == NULL) goto bad; } else if (PyExceptionInstance_Check(cause)) { fixed_cause = cause; Py_INCREF(fixed_cause); } else { PyErr_SetString(PyExc_TypeError, "exception causes must derive from " "BaseException"); goto bad; } if (!value) { value = PyObject_CallObject(type, NULL); } PyException_SetCause(value, fixed_cause); } PyErr_SetObject(type, value); if (tb) { PyThreadState *tstate = PyThreadState_GET(); PyObject* tmp_tb = tstate->curexc_traceback; if (tb != tmp_tb) { Py_INCREF(tb); tstate->curexc_traceback = tb; Py_XDECREF(tmp_tb); } } bad: return; } #endif static CYTHON_INLINE void __Pyx_RaiseNeedMoreValuesError(Py_ssize_t index) { PyErr_Format(PyExc_ValueError, "need more than %"PY_FORMAT_SIZE_T"d value%s to unpack", index, (index == 1) ? "" : "s"); } static CYTHON_INLINE void __Pyx_RaiseTooManyValuesError(Py_ssize_t expected) { PyErr_Format(PyExc_ValueError, "too many values to unpack (expected %"PY_FORMAT_SIZE_T"d)", expected); } static CYTHON_INLINE void __Pyx_RaiseNoneNotIterableError(void) { PyErr_SetString(PyExc_TypeError, "'NoneType' object is not iterable"); } static void __Pyx_UnpackTupleError(PyObject *t, Py_ssize_t index) { if (t == Py_None) { __Pyx_RaiseNoneNotIterableError(); } else if (PyTuple_GET_SIZE(t) < index) { __Pyx_RaiseNeedMoreValuesError(PyTuple_GET_SIZE(t)); } else { __Pyx_RaiseTooManyValuesError(index); } } #if PY_MAJOR_VERSION < 3 static int __Pyx_GetBuffer(PyObject *obj, Py_buffer *view, int flags) { PyObject *getbuffer_cobj; #if PY_VERSION_HEX >= 0x02060000 if (PyObject_CheckBuffer(obj)) return PyObject_GetBuffer(obj, view, flags); #endif if (PyObject_TypeCheck(obj, __pyx_ptype_5numpy_ndarray)) return __pyx_pw_5numpy_7ndarray_1__getbuffer__(obj, view, flags); #if PY_VERSION_HEX < 0x02060000 if (obj->ob_type->tp_dict && (getbuffer_cobj = PyMapping_GetItemString(obj->ob_type->tp_dict, "__pyx_getbuffer"))) { getbufferproc func; #if PY_VERSION_HEX >= 0x02070000 && !(PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION == 0) func = (getbufferproc) PyCapsule_GetPointer(getbuffer_cobj, "getbuffer(obj, view, flags)"); #else func = (getbufferproc) PyCObject_AsVoidPtr(getbuffer_cobj); #endif Py_DECREF(getbuffer_cobj); if (!func) goto fail; return func(obj, view, flags); } else { PyErr_Clear(); } #endif PyErr_Format(PyExc_TypeError, "'%100s' does not have the buffer interface", Py_TYPE(obj)->tp_name); #if PY_VERSION_HEX < 0x02060000 fail: #endif return -1; } static void __Pyx_ReleaseBuffer(Py_buffer *view) { PyObject *obj = view->obj; PyObject *releasebuffer_cobj; if (!obj) return; #if PY_VERSION_HEX >= 0x02060000 if (PyObject_CheckBuffer(obj)) { PyBuffer_Release(view); return; } #endif if (PyObject_TypeCheck(obj, __pyx_ptype_5numpy_ndarray)) { __pyx_pw_5numpy_7ndarray_3__releasebuffer__(obj, view); return; } #if PY_VERSION_HEX < 0x02060000 if (obj->ob_type->tp_dict && (releasebuffer_cobj = PyMapping_GetItemString(obj->ob_type->tp_dict, "__pyx_releasebuffer"))) { releasebufferproc func; #if PY_VERSION_HEX >= 0x02070000 && !(PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION == 0) func = (releasebufferproc) PyCapsule_GetPointer(releasebuffer_cobj, "releasebuffer(obj, view)"); #else func = (releasebufferproc) PyCObject_AsVoidPtr(releasebuffer_cobj); #endif Py_DECREF(releasebuffer_cobj); if (!func) goto fail; func(obj, view); return; } else { PyErr_Clear(); } #endif goto nofail; #if PY_VERSION_HEX < 0x02060000 fail: #endif PyErr_WriteUnraisable(obj); nofail: Py_DECREF(obj); view->obj = NULL; } #endif /* PY_MAJOR_VERSION < 3 */ static PyObject *__Pyx_Import(PyObject *name, PyObject *from_list, long level) { PyObject *py_import = 0; PyObject *empty_list = 0; PyObject *module = 0; PyObject *global_dict = 0; PyObject *empty_dict = 0; PyObject *list; py_import = __Pyx_GetAttrString(__pyx_b, "__import__"); if (!py_import) goto bad; if (from_list) list = from_list; else { empty_list = PyList_New(0); if (!empty_list) goto bad; list = empty_list; } global_dict = PyModule_GetDict(__pyx_m); if (!global_dict) goto bad; empty_dict = PyDict_New(); if (!empty_dict) goto bad; #if PY_VERSION_HEX >= 0x02050000 { #if PY_MAJOR_VERSION >= 3 if (level == -1) { if (strchr(__Pyx_MODULE_NAME, '.')) { /* try package relative import first */ PyObject *py_level = PyInt_FromLong(1); if (!py_level) goto bad; module = PyObject_CallFunctionObjArgs(py_import, name, global_dict, empty_dict, list, py_level, NULL); Py_DECREF(py_level); if (!module) { if (!PyErr_ExceptionMatches(PyExc_ImportError)) goto bad; PyErr_Clear(); } } level = 0; /* try absolute import on failure */ } #endif if (!module) { PyObject *py_level = PyInt_FromLong(level); if (!py_level) goto bad; module = PyObject_CallFunctionObjArgs(py_import, name, global_dict, empty_dict, list, py_level, NULL); Py_DECREF(py_level); } } #else if (level>0) { PyErr_SetString(PyExc_RuntimeError, "Relative import is not supported for Python <=2.4."); goto bad; } module = PyObject_CallFunctionObjArgs(py_import, name, global_dict, empty_dict, list, NULL); #endif bad: Py_XDECREF(empty_list); Py_XDECREF(py_import); Py_XDECREF(empty_dict); return module; } #if CYTHON_CCOMPLEX #ifdef __cplusplus static CYTHON_INLINE __pyx_t_float_complex __pyx_t_float_complex_from_parts(float x, float y) { return ::std::complex< float >(x, y); } #else static CYTHON_INLINE __pyx_t_float_complex __pyx_t_float_complex_from_parts(float x, float y) { return x + y*(__pyx_t_float_complex)_Complex_I; } #endif #else static CYTHON_INLINE __pyx_t_float_complex __pyx_t_float_complex_from_parts(float x, float y) { __pyx_t_float_complex z; z.real = x; z.imag = y; return z; } #endif #if CYTHON_CCOMPLEX #else static CYTHON_INLINE int __Pyx_c_eqf(__pyx_t_float_complex a, __pyx_t_float_complex b) { return (a.real == b.real) && (a.imag == b.imag); } static CYTHON_INLINE __pyx_t_float_complex __Pyx_c_sumf(__pyx_t_float_complex a, __pyx_t_float_complex b) { __pyx_t_float_complex z; z.real = a.real + b.real; z.imag = a.imag + b.imag; return z; } static CYTHON_INLINE __pyx_t_float_complex __Pyx_c_difff(__pyx_t_float_complex a, __pyx_t_float_complex b) { __pyx_t_float_complex z; z.real = a.real - b.real; z.imag = a.imag - b.imag; return z; } static CYTHON_INLINE __pyx_t_float_complex __Pyx_c_prodf(__pyx_t_float_complex a, __pyx_t_float_complex b) { __pyx_t_float_complex z; z.real = a.real * b.real - a.imag * b.imag; z.imag = a.real * b.imag + a.imag * b.real; return z; } static CYTHON_INLINE __pyx_t_float_complex __Pyx_c_quotf(__pyx_t_float_complex a, __pyx_t_float_complex b) { __pyx_t_float_complex z; float denom = b.real * b.real + b.imag * b.imag; z.real = (a.real * b.real + a.imag * b.imag) / denom; z.imag = (a.imag * b.real - a.real * b.imag) / denom; return z; } static CYTHON_INLINE __pyx_t_float_complex __Pyx_c_negf(__pyx_t_float_complex a) { __pyx_t_float_complex z; z.real = -a.real; z.imag = -a.imag; return z; } static CYTHON_INLINE int __Pyx_c_is_zerof(__pyx_t_float_complex a) { return (a.real == 0) && (a.imag == 0); } static CYTHON_INLINE __pyx_t_float_complex __Pyx_c_conjf(__pyx_t_float_complex a) { __pyx_t_float_complex z; z.real = a.real; z.imag = -a.imag; return z; } #if 1 static CYTHON_INLINE float __Pyx_c_absf(__pyx_t_float_complex z) { #if !defined(HAVE_HYPOT) || defined(_MSC_VER) return sqrtf(z.real*z.real + z.imag*z.imag); #else return hypotf(z.real, z.imag); #endif } static CYTHON_INLINE __pyx_t_float_complex __Pyx_c_powf(__pyx_t_float_complex a, __pyx_t_float_complex b) { __pyx_t_float_complex z; float r, lnr, theta, z_r, z_theta; if (b.imag == 0 && b.real == (int)b.real) { if (b.real < 0) { float denom = a.real * a.real + a.imag * a.imag; a.real = a.real / denom; a.imag = -a.imag / denom; b.real = -b.real; } switch ((int)b.real) { case 0: z.real = 1; z.imag = 0; return z; case 1: return a; case 2: z = __Pyx_c_prodf(a, a); return __Pyx_c_prodf(a, a); case 3: z = __Pyx_c_prodf(a, a); return __Pyx_c_prodf(z, a); case 4: z = __Pyx_c_prodf(a, a); return __Pyx_c_prodf(z, z); } } if (a.imag == 0) { if (a.real == 0) { return a; } r = a.real; theta = 0; } else { r = __Pyx_c_absf(a); theta = atan2f(a.imag, a.real); } lnr = logf(r); z_r = expf(lnr * b.real - theta * b.imag); z_theta = theta * b.real + lnr * b.imag; z.real = z_r * cosf(z_theta); z.imag = z_r * sinf(z_theta); return z; } #endif #endif #if CYTHON_CCOMPLEX #ifdef __cplusplus static CYTHON_INLINE __pyx_t_double_complex __pyx_t_double_complex_from_parts(double x, double y) { return ::std::complex< double >(x, y); } #else static CYTHON_INLINE __pyx_t_double_complex __pyx_t_double_complex_from_parts(double x, double y) { return x + y*(__pyx_t_double_complex)_Complex_I; } #endif #else static CYTHON_INLINE __pyx_t_double_complex __pyx_t_double_complex_from_parts(double x, double y) { __pyx_t_double_complex z; z.real = x; z.imag = y; return z; } #endif #if CYTHON_CCOMPLEX #else static CYTHON_INLINE int __Pyx_c_eq(__pyx_t_double_complex a, __pyx_t_double_complex b) { return (a.real == b.real) && (a.imag == b.imag); } static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_sum(__pyx_t_double_complex a, __pyx_t_double_complex b) { __pyx_t_double_complex z; z.real = a.real + b.real; z.imag = a.imag + b.imag; return z; } static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_diff(__pyx_t_double_complex a, __pyx_t_double_complex b) { __pyx_t_double_complex z; z.real = a.real - b.real; z.imag = a.imag - b.imag; return z; } static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_prod(__pyx_t_double_complex a, __pyx_t_double_complex b) { __pyx_t_double_complex z; z.real = a.real * b.real - a.imag * b.imag; z.imag = a.real * b.imag + a.imag * b.real; return z; } static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_quot(__pyx_t_double_complex a, __pyx_t_double_complex b) { __pyx_t_double_complex z; double denom = b.real * b.real + b.imag * b.imag; z.real = (a.real * b.real + a.imag * b.imag) / denom; z.imag = (a.imag * b.real - a.real * b.imag) / denom; return z; } static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_neg(__pyx_t_double_complex a) { __pyx_t_double_complex z; z.real = -a.real; z.imag = -a.imag; return z; } static CYTHON_INLINE int __Pyx_c_is_zero(__pyx_t_double_complex a) { return (a.real == 0) && (a.imag == 0); } static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_conj(__pyx_t_double_complex a) { __pyx_t_double_complex z; z.real = a.real; z.imag = -a.imag; return z; } #if 1 static CYTHON_INLINE double __Pyx_c_abs(__pyx_t_double_complex z) { #if !defined(HAVE_HYPOT) || defined(_MSC_VER) return sqrt(z.real*z.real + z.imag*z.imag); #else return hypot(z.real, z.imag); #endif } static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_pow(__pyx_t_double_complex a, __pyx_t_double_complex b) { __pyx_t_double_complex z; double r, lnr, theta, z_r, z_theta; if (b.imag == 0 && b.real == (int)b.real) { if (b.real < 0) { double denom = a.real * a.real + a.imag * a.imag; a.real = a.real / denom; a.imag = -a.imag / denom; b.real = -b.real; } switch ((int)b.real) { case 0: z.real = 1; z.imag = 0; return z; case 1: return a; case 2: z = __Pyx_c_prod(a, a); return __Pyx_c_prod(a, a); case 3: z = __Pyx_c_prod(a, a); return __Pyx_c_prod(z, a); case 4: z = __Pyx_c_prod(a, a); return __Pyx_c_prod(z, z); } } if (a.imag == 0) { if (a.real == 0) { return a; } r = a.real; theta = 0; } else { r = __Pyx_c_abs(a); theta = atan2(a.imag, a.real); } lnr = log(r); z_r = exp(lnr * b.real - theta * b.imag); z_theta = theta * b.real + lnr * b.imag; z.real = z_r * cos(z_theta); z.imag = z_r * sin(z_theta); return z; } #endif #endif static CYTHON_INLINE unsigned char __Pyx_PyInt_AsUnsignedChar(PyObject* x) { const unsigned char neg_one = (unsigned char)-1, const_zero = 0; const int is_unsigned = neg_one > const_zero; if (sizeof(unsigned char) < sizeof(long)) { long val = __Pyx_PyInt_AsLong(x); if (unlikely(val != (long)(unsigned char)val)) { if (!unlikely(val == -1 && PyErr_Occurred())) { PyErr_SetString(PyExc_OverflowError, (is_unsigned && unlikely(val < 0)) ? "can't convert negative value to unsigned char" : "value too large to convert to unsigned char"); } return (unsigned char)-1; } return (unsigned char)val; } return (unsigned char)__Pyx_PyInt_AsUnsignedLong(x); } static CYTHON_INLINE unsigned short __Pyx_PyInt_AsUnsignedShort(PyObject* x) { const unsigned short neg_one = (unsigned short)-1, const_zero = 0; const int is_unsigned = neg_one > const_zero; if (sizeof(unsigned short) < sizeof(long)) { long val = __Pyx_PyInt_AsLong(x); if (unlikely(val != (long)(unsigned short)val)) { if (!unlikely(val == -1 && PyErr_Occurred())) { PyErr_SetString(PyExc_OverflowError, (is_unsigned && unlikely(val < 0)) ? "can't convert negative value to unsigned short" : "value too large to convert to unsigned short"); } return (unsigned short)-1; } return (unsigned short)val; } return (unsigned short)__Pyx_PyInt_AsUnsignedLong(x); } static CYTHON_INLINE unsigned int __Pyx_PyInt_AsUnsignedInt(PyObject* x) { const unsigned int neg_one = (unsigned int)-1, const_zero = 0; const int is_unsigned = neg_one > const_zero; if (sizeof(unsigned int) < sizeof(long)) { long val = __Pyx_PyInt_AsLong(x); if (unlikely(val != (long)(unsigned int)val)) { if (!unlikely(val == -1 && PyErr_Occurred())) { PyErr_SetString(PyExc_OverflowError, (is_unsigned && unlikely(val < 0)) ? "can't convert negative value to unsigned int" : "value too large to convert to unsigned int"); } return (unsigned int)-1; } return (unsigned int)val; } return (unsigned int)__Pyx_PyInt_AsUnsignedLong(x); } static CYTHON_INLINE char __Pyx_PyInt_AsChar(PyObject* x) { const char neg_one = (char)-1, const_zero = 0; const int is_unsigned = neg_one > const_zero; if (sizeof(char) < sizeof(long)) { long val = __Pyx_PyInt_AsLong(x); if (unlikely(val != (long)(char)val)) { if (!unlikely(val == -1 && PyErr_Occurred())) { PyErr_SetString(PyExc_OverflowError, (is_unsigned && unlikely(val < 0)) ? "can't convert negative value to char" : "value too large to convert to char"); } return (char)-1; } return (char)val; } return (char)__Pyx_PyInt_AsLong(x); } static CYTHON_INLINE short __Pyx_PyInt_AsShort(PyObject* x) { const short neg_one = (short)-1, const_zero = 0; const int is_unsigned = neg_one > const_zero; if (sizeof(short) < sizeof(long)) { long val = __Pyx_PyInt_AsLong(x); if (unlikely(val != (long)(short)val)) { if (!unlikely(val == -1 && PyErr_Occurred())) { PyErr_SetString(PyExc_OverflowError, (is_unsigned && unlikely(val < 0)) ? "can't convert negative value to short" : "value too large to convert to short"); } return (short)-1; } return (short)val; } return (short)__Pyx_PyInt_AsLong(x); } static CYTHON_INLINE int __Pyx_PyInt_AsInt(PyObject* x) { const int neg_one = (int)-1, const_zero = 0; const int is_unsigned = neg_one > const_zero; if (sizeof(int) < sizeof(long)) { long val = __Pyx_PyInt_AsLong(x); if (unlikely(val != (long)(int)val)) { if (!unlikely(val == -1 && PyErr_Occurred())) { PyErr_SetString(PyExc_OverflowError, (is_unsigned && unlikely(val < 0)) ? "can't convert negative value to int" : "value too large to convert to int"); } return (int)-1; } return (int)val; } return (int)__Pyx_PyInt_AsLong(x); } static CYTHON_INLINE signed char __Pyx_PyInt_AsSignedChar(PyObject* x) { const signed char neg_one = (signed char)-1, const_zero = 0; const int is_unsigned = neg_one > const_zero; if (sizeof(signed char) < sizeof(long)) { long val = __Pyx_PyInt_AsLong(x); if (unlikely(val != (long)(signed char)val)) { if (!unlikely(val == -1 && PyErr_Occurred())) { PyErr_SetString(PyExc_OverflowError, (is_unsigned && unlikely(val < 0)) ? "can't convert negative value to signed char" : "value too large to convert to signed char"); } return (signed char)-1; } return (signed char)val; } return (signed char)__Pyx_PyInt_AsSignedLong(x); } static CYTHON_INLINE signed short __Pyx_PyInt_AsSignedShort(PyObject* x) { const signed short neg_one = (signed short)-1, const_zero = 0; const int is_unsigned = neg_one > const_zero; if (sizeof(signed short) < sizeof(long)) { long val = __Pyx_PyInt_AsLong(x); if (unlikely(val != (long)(signed short)val)) { if (!unlikely(val == -1 && PyErr_Occurred())) { PyErr_SetString(PyExc_OverflowError, (is_unsigned && unlikely(val < 0)) ? "can't convert negative value to signed short" : "value too large to convert to signed short"); } return (signed short)-1; } return (signed short)val; } return (signed short)__Pyx_PyInt_AsSignedLong(x); } static CYTHON_INLINE signed int __Pyx_PyInt_AsSignedInt(PyObject* x) { const signed int neg_one = (signed int)-1, const_zero = 0; const int is_unsigned = neg_one > const_zero; if (sizeof(signed int) < sizeof(long)) { long val = __Pyx_PyInt_AsLong(x); if (unlikely(val != (long)(signed int)val)) { if (!unlikely(val == -1 && PyErr_Occurred())) { PyErr_SetString(PyExc_OverflowError, (is_unsigned && unlikely(val < 0)) ? "can't convert negative value to signed int" : "value too large to convert to signed int"); } return (signed int)-1; } return (signed int)val; } return (signed int)__Pyx_PyInt_AsSignedLong(x); } static CYTHON_INLINE int __Pyx_PyInt_AsLongDouble(PyObject* x) { const int neg_one = (int)-1, const_zero = 0; const int is_unsigned = neg_one > const_zero; if (sizeof(int) < sizeof(long)) { long val = __Pyx_PyInt_AsLong(x); if (unlikely(val != (long)(int)val)) { if (!unlikely(val == -1 && PyErr_Occurred())) { PyErr_SetString(PyExc_OverflowError, (is_unsigned && unlikely(val < 0)) ? "can't convert negative value to int" : "value too large to convert to int"); } return (int)-1; } return (int)val; } return (int)__Pyx_PyInt_AsLong(x); } static CYTHON_INLINE unsigned long __Pyx_PyInt_AsUnsignedLong(PyObject* x) { const unsigned long neg_one = (unsigned long)-1, const_zero = 0; const int is_unsigned = neg_one > const_zero; #if PY_VERSION_HEX < 0x03000000 if (likely(PyInt_Check(x))) { long val = PyInt_AS_LONG(x); if (is_unsigned && unlikely(val < 0)) { PyErr_SetString(PyExc_OverflowError, "can't convert negative value to unsigned long"); return (unsigned long)-1; } return (unsigned long)val; } else #endif if (likely(PyLong_Check(x))) { if (is_unsigned) { if (unlikely(Py_SIZE(x) < 0)) { PyErr_SetString(PyExc_OverflowError, "can't convert negative value to unsigned long"); return (unsigned long)-1; } return (unsigned long)PyLong_AsUnsignedLong(x); } else { return (unsigned long)PyLong_AsLong(x); } } else { unsigned long val; PyObject *tmp = __Pyx_PyNumber_Int(x); if (!tmp) return (unsigned long)-1; val = __Pyx_PyInt_AsUnsignedLong(tmp); Py_DECREF(tmp); return val; } } static CYTHON_INLINE unsigned PY_LONG_LONG __Pyx_PyInt_AsUnsignedLongLong(PyObject* x) { const unsigned PY_LONG_LONG neg_one = (unsigned PY_LONG_LONG)-1, const_zero = 0; const int is_unsigned = neg_one > const_zero; #if PY_VERSION_HEX < 0x03000000 if (likely(PyInt_Check(x))) { long val = PyInt_AS_LONG(x); if (is_unsigned && unlikely(val < 0)) { PyErr_SetString(PyExc_OverflowError, "can't convert negative value to unsigned PY_LONG_LONG"); return (unsigned PY_LONG_LONG)-1; } return (unsigned PY_LONG_LONG)val; } else #endif if (likely(PyLong_Check(x))) { if (is_unsigned) { if (unlikely(Py_SIZE(x) < 0)) { PyErr_SetString(PyExc_OverflowError, "can't convert negative value to unsigned PY_LONG_LONG"); return (unsigned PY_LONG_LONG)-1; } return (unsigned PY_LONG_LONG)PyLong_AsUnsignedLongLong(x); } else { return (unsigned PY_LONG_LONG)PyLong_AsLongLong(x); } } else { unsigned PY_LONG_LONG val; PyObject *tmp = __Pyx_PyNumber_Int(x); if (!tmp) return (unsigned PY_LONG_LONG)-1; val = __Pyx_PyInt_AsUnsignedLongLong(tmp); Py_DECREF(tmp); return val; } } static CYTHON_INLINE long __Pyx_PyInt_AsLong(PyObject* x) { const long neg_one = (long)-1, const_zero = 0; const int is_unsigned = neg_one > const_zero; #if PY_VERSION_HEX < 0x03000000 if (likely(PyInt_Check(x))) { long val = PyInt_AS_LONG(x); if (is_unsigned && unlikely(val < 0)) { PyErr_SetString(PyExc_OverflowError, "can't convert negative value to long"); return (long)-1; } return (long)val; } else #endif if (likely(PyLong_Check(x))) { if (is_unsigned) { if (unlikely(Py_SIZE(x) < 0)) { PyErr_SetString(PyExc_OverflowError, "can't convert negative value to long"); return (long)-1; } return (long)PyLong_AsUnsignedLong(x); } else { return (long)PyLong_AsLong(x); } } else { long val; PyObject *tmp = __Pyx_PyNumber_Int(x); if (!tmp) return (long)-1; val = __Pyx_PyInt_AsLong(tmp); Py_DECREF(tmp); return val; } } static CYTHON_INLINE PY_LONG_LONG __Pyx_PyInt_AsLongLong(PyObject* x) { const PY_LONG_LONG neg_one = (PY_LONG_LONG)-1, const_zero = 0; const int is_unsigned = neg_one > const_zero; #if PY_VERSION_HEX < 0x03000000 if (likely(PyInt_Check(x))) { long val = PyInt_AS_LONG(x); if (is_unsigned && unlikely(val < 0)) { PyErr_SetString(PyExc_OverflowError, "can't convert negative value to PY_LONG_LONG"); return (PY_LONG_LONG)-1; } return (PY_LONG_LONG)val; } else #endif if (likely(PyLong_Check(x))) { if (is_unsigned) { if (unlikely(Py_SIZE(x) < 0)) { PyErr_SetString(PyExc_OverflowError, "can't convert negative value to PY_LONG_LONG"); return (PY_LONG_LONG)-1; } return (PY_LONG_LONG)PyLong_AsUnsignedLongLong(x); } else { return (PY_LONG_LONG)PyLong_AsLongLong(x); } } else { PY_LONG_LONG val; PyObject *tmp = __Pyx_PyNumber_Int(x); if (!tmp) return (PY_LONG_LONG)-1; val = __Pyx_PyInt_AsLongLong(tmp); Py_DECREF(tmp); return val; } } static CYTHON_INLINE signed long __Pyx_PyInt_AsSignedLong(PyObject* x) { const signed long neg_one = (signed long)-1, const_zero = 0; const int is_unsigned = neg_one > const_zero; #if PY_VERSION_HEX < 0x03000000 if (likely(PyInt_Check(x))) { long val = PyInt_AS_LONG(x); if (is_unsigned && unlikely(val < 0)) { PyErr_SetString(PyExc_OverflowError, "can't convert negative value to signed long"); return (signed long)-1; } return (signed long)val; } else #endif if (likely(PyLong_Check(x))) { if (is_unsigned) { if (unlikely(Py_SIZE(x) < 0)) { PyErr_SetString(PyExc_OverflowError, "can't convert negative value to signed long"); return (signed long)-1; } return (signed long)PyLong_AsUnsignedLong(x); } else { return (signed long)PyLong_AsLong(x); } } else { signed long val; PyObject *tmp = __Pyx_PyNumber_Int(x); if (!tmp) return (signed long)-1; val = __Pyx_PyInt_AsSignedLong(tmp); Py_DECREF(tmp); return val; } } static CYTHON_INLINE signed PY_LONG_LONG __Pyx_PyInt_AsSignedLongLong(PyObject* x) { const signed PY_LONG_LONG neg_one = (signed PY_LONG_LONG)-1, const_zero = 0; const int is_unsigned = neg_one > const_zero; #if PY_VERSION_HEX < 0x03000000 if (likely(PyInt_Check(x))) { long val = PyInt_AS_LONG(x); if (is_unsigned && unlikely(val < 0)) { PyErr_SetString(PyExc_OverflowError, "can't convert negative value to signed PY_LONG_LONG"); return (signed PY_LONG_LONG)-1; } return (signed PY_LONG_LONG)val; } else #endif if (likely(PyLong_Check(x))) { if (is_unsigned) { if (unlikely(Py_SIZE(x) < 0)) { PyErr_SetString(PyExc_OverflowError, "can't convert negative value to signed PY_LONG_LONG"); return (signed PY_LONG_LONG)-1; } return (signed PY_LONG_LONG)PyLong_AsUnsignedLongLong(x); } else { return (signed PY_LONG_LONG)PyLong_AsLongLong(x); } } else { signed PY_LONG_LONG val; PyObject *tmp = __Pyx_PyNumber_Int(x); if (!tmp) return (signed PY_LONG_LONG)-1; val = __Pyx_PyInt_AsSignedLongLong(tmp); Py_DECREF(tmp); return val; } } static int __Pyx_check_binary_version(void) { char ctversion[4], rtversion[4]; PyOS_snprintf(ctversion, 4, "%d.%d", PY_MAJOR_VERSION, PY_MINOR_VERSION); PyOS_snprintf(rtversion, 4, "%s", Py_GetVersion()); if (ctversion[0] != rtversion[0] || ctversion[2] != rtversion[2]) { char message[200]; PyOS_snprintf(message, sizeof(message), "compiletime version %s of module '%.100s' " "does not match runtime version %s", ctversion, __Pyx_MODULE_NAME, rtversion); #if PY_VERSION_HEX < 0x02050000 return PyErr_Warn(NULL, message); #else return PyErr_WarnEx(NULL, message, 1); #endif } return 0; } #ifndef __PYX_HAVE_RT_ImportType #define __PYX_HAVE_RT_ImportType static PyTypeObject *__Pyx_ImportType(const char *module_name, const char *class_name, size_t size, int strict) { PyObject *py_module = 0; PyObject *result = 0; PyObject *py_name = 0; char warning[200]; py_module = __Pyx_ImportModule(module_name); if (!py_module) goto bad; py_name = __Pyx_PyIdentifier_FromString(class_name); if (!py_name) goto bad; result = PyObject_GetAttr(py_module, py_name); Py_DECREF(py_name); py_name = 0; Py_DECREF(py_module); py_module = 0; if (!result) goto bad; if (!PyType_Check(result)) { PyErr_Format(PyExc_TypeError, "%s.%s is not a type object", module_name, class_name); goto bad; } if (!strict && (size_t)((PyTypeObject *)result)->tp_basicsize > size) { PyOS_snprintf(warning, sizeof(warning), "%s.%s size changed, may indicate binary incompatibility", module_name, class_name); #if PY_VERSION_HEX < 0x02050000 if (PyErr_Warn(NULL, warning) < 0) goto bad; #else if (PyErr_WarnEx(NULL, warning, 0) < 0) goto bad; #endif } else if ((size_t)((PyTypeObject *)result)->tp_basicsize != size) { PyErr_Format(PyExc_ValueError, "%s.%s has the wrong size, try recompiling", module_name, class_name); goto bad; } return (PyTypeObject *)result; bad: Py_XDECREF(py_module); Py_XDECREF(result); return NULL; } #endif #ifndef __PYX_HAVE_RT_ImportModule #define __PYX_HAVE_RT_ImportModule static PyObject *__Pyx_ImportModule(const char *name) { PyObject *py_name = 0; PyObject *py_module = 0; py_name = __Pyx_PyIdentifier_FromString(name); if (!py_name) goto bad; py_module = PyImport_Import(py_name); Py_DECREF(py_name); return py_module; bad: Py_XDECREF(py_name); return 0; } #endif static int __pyx_bisect_code_objects(__Pyx_CodeObjectCacheEntry* entries, int count, int code_line) { int start = 0, mid = 0, end = count - 1; if (end >= 0 && code_line > entries[end].code_line) { return count; } while (start < end) { mid = (start + end) / 2; if (code_line < entries[mid].code_line) { end = mid; } else if (code_line > entries[mid].code_line) { start = mid + 1; } else { return mid; } } if (code_line <= entries[mid].code_line) { return mid; } else { return mid + 1; } } static PyCodeObject *__pyx_find_code_object(int code_line) { PyCodeObject* code_object; int pos; if (unlikely(!code_line) || unlikely(!__pyx_code_cache.entries)) { return NULL; } pos = __pyx_bisect_code_objects(__pyx_code_cache.entries, __pyx_code_cache.count, code_line); if (unlikely(pos >= __pyx_code_cache.count) || unlikely(__pyx_code_cache.entries[pos].code_line != code_line)) { return NULL; } code_object = __pyx_code_cache.entries[pos].code_object; Py_INCREF(code_object); return code_object; } static void __pyx_insert_code_object(int code_line, PyCodeObject* code_object) { int pos, i; __Pyx_CodeObjectCacheEntry* entries = __pyx_code_cache.entries; if (unlikely(!code_line)) { return; } if (unlikely(!entries)) { entries = (__Pyx_CodeObjectCacheEntry*)PyMem_Malloc(64*sizeof(__Pyx_CodeObjectCacheEntry)); if (likely(entries)) { __pyx_code_cache.entries = entries; __pyx_code_cache.max_count = 64; __pyx_code_cache.count = 1; entries[0].code_line = code_line; entries[0].code_object = code_object; Py_INCREF(code_object); } return; } pos = __pyx_bisect_code_objects(__pyx_code_cache.entries, __pyx_code_cache.count, code_line); if ((pos < __pyx_code_cache.count) && unlikely(__pyx_code_cache.entries[pos].code_line == code_line)) { PyCodeObject* tmp = entries[pos].code_object; entries[pos].code_object = code_object; Py_DECREF(tmp); return; } if (__pyx_code_cache.count == __pyx_code_cache.max_count) { int new_max = __pyx_code_cache.max_count + 64; entries = (__Pyx_CodeObjectCacheEntry*)PyMem_Realloc( __pyx_code_cache.entries, new_max*sizeof(__Pyx_CodeObjectCacheEntry)); if (unlikely(!entries)) { return; } __pyx_code_cache.entries = entries; __pyx_code_cache.max_count = new_max; } for (i=__pyx_code_cache.count; i>pos; i--) { entries[i] = entries[i-1]; } entries[pos].code_line = code_line; entries[pos].code_object = code_object; __pyx_code_cache.count++; Py_INCREF(code_object); } #include "compile.h" #include "frameobject.h" #include "traceback.h" static PyCodeObject* __Pyx_CreateCodeObjectForTraceback( const char *funcname, int c_line, int py_line, const char *filename) { PyCodeObject *py_code = 0; PyObject *py_srcfile = 0; PyObject *py_funcname = 0; #if PY_MAJOR_VERSION < 3 py_srcfile = PyString_FromString(filename); #else py_srcfile = PyUnicode_FromString(filename); #endif if (!py_srcfile) goto bad; if (c_line) { #if PY_MAJOR_VERSION < 3 py_funcname = PyString_FromFormat( "%s (%s:%d)", funcname, __pyx_cfilenm, c_line); #else py_funcname = PyUnicode_FromFormat( "%s (%s:%d)", funcname, __pyx_cfilenm, c_line); #endif } else { #if PY_MAJOR_VERSION < 3 py_funcname = PyString_FromString(funcname); #else py_funcname = PyUnicode_FromString(funcname); #endif } if (!py_funcname) goto bad; py_code = __Pyx_PyCode_New( 0, /*int argcount,*/ 0, /*int kwonlyargcount,*/ 0, /*int nlocals,*/ 0, /*int stacksize,*/ 0, /*int flags,*/ __pyx_empty_bytes, /*PyObject *code,*/ __pyx_empty_tuple, /*PyObject *consts,*/ __pyx_empty_tuple, /*PyObject *names,*/ __pyx_empty_tuple, /*PyObject *varnames,*/ __pyx_empty_tuple, /*PyObject *freevars,*/ __pyx_empty_tuple, /*PyObject *cellvars,*/ py_srcfile, /*PyObject *filename,*/ py_funcname, /*PyObject *name,*/ py_line, /*int firstlineno,*/ __pyx_empty_bytes /*PyObject *lnotab*/ ); Py_DECREF(py_srcfile); Py_DECREF(py_funcname); return py_code; bad: Py_XDECREF(py_srcfile); Py_XDECREF(py_funcname); return NULL; } static void __Pyx_AddTraceback(const char *funcname, int c_line, int py_line, const char *filename) { PyCodeObject *py_code = 0; PyObject *py_globals = 0; PyFrameObject *py_frame = 0; py_code = __pyx_find_code_object(c_line ? c_line : py_line); if (!py_code) { py_code = __Pyx_CreateCodeObjectForTraceback( funcname, c_line, py_line, filename); if (!py_code) goto bad; __pyx_insert_code_object(c_line ? c_line : py_line, py_code); } py_globals = PyModule_GetDict(__pyx_m); if (!py_globals) goto bad; py_frame = PyFrame_New( PyThreadState_GET(), /*PyThreadState *tstate,*/ py_code, /*PyCodeObject *code,*/ py_globals, /*PyObject *globals,*/ 0 /*PyObject *locals*/ ); if (!py_frame) goto bad; py_frame->f_lineno = py_line; PyTraceBack_Here(py_frame); bad: Py_XDECREF(py_code); Py_XDECREF(py_frame); } static int __Pyx_InitStrings(__Pyx_StringTabEntry *t) { while (t->p) { #if PY_MAJOR_VERSION < 3 if (t->is_unicode) { *t->p = PyUnicode_DecodeUTF8(t->s, t->n - 1, NULL); } else if (t->intern) { *t->p = PyString_InternFromString(t->s); } else { *t->p = PyString_FromStringAndSize(t->s, t->n - 1); } #else /* Python 3+ has unicode identifiers */ if (t->is_unicode | t->is_str) { if (t->intern) { *t->p = PyUnicode_InternFromString(t->s); } else if (t->encoding) { *t->p = PyUnicode_Decode(t->s, t->n - 1, t->encoding, NULL); } else { *t->p = PyUnicode_FromStringAndSize(t->s, t->n - 1); } } else { *t->p = PyBytes_FromStringAndSize(t->s, t->n - 1); } #endif if (!*t->p) return -1; ++t; } return 0; } /* Type Conversion Functions */ static CYTHON_INLINE int __Pyx_PyObject_IsTrue(PyObject* x) { int is_true = x == Py_True; if (is_true | (x == Py_False) | (x == Py_None)) return is_true; else return PyObject_IsTrue(x); } static CYTHON_INLINE PyObject* __Pyx_PyNumber_Int(PyObject* x) { PyNumberMethods *m; const char *name = NULL; PyObject *res = NULL; #if PY_VERSION_HEX < 0x03000000 if (PyInt_Check(x) || PyLong_Check(x)) #else if (PyLong_Check(x)) #endif return Py_INCREF(x), x; m = Py_TYPE(x)->tp_as_number; #if PY_VERSION_HEX < 0x03000000 if (m && m->nb_int) { name = "int"; res = PyNumber_Int(x); } else if (m && m->nb_long) { name = "long"; res = PyNumber_Long(x); } #else if (m && m->nb_int) { name = "int"; res = PyNumber_Long(x); } #endif if (res) { #if PY_VERSION_HEX < 0x03000000 if (!PyInt_Check(res) && !PyLong_Check(res)) { #else if (!PyLong_Check(res)) { #endif PyErr_Format(PyExc_TypeError, "__%s__ returned non-%s (type %.200s)", name, name, Py_TYPE(res)->tp_name); Py_DECREF(res); return NULL; } } else if (!PyErr_Occurred()) { PyErr_SetString(PyExc_TypeError, "an integer is required"); } return res; } static CYTHON_INLINE Py_ssize_t __Pyx_PyIndex_AsSsize_t(PyObject* b) { Py_ssize_t ival; PyObject* x = PyNumber_Index(b); if (!x) return -1; ival = PyInt_AsSsize_t(x); Py_DECREF(x); return ival; } static CYTHON_INLINE PyObject * __Pyx_PyInt_FromSize_t(size_t ival) { #if PY_VERSION_HEX < 0x02050000 if (ival <= LONG_MAX) return PyInt_FromLong((long)ival); else { unsigned char *bytes = (unsigned char *) &ival; int one = 1; int little = (int)*(unsigned char*)&one; return _PyLong_FromByteArray(bytes, sizeof(size_t), little, 0); } #else return PyInt_FromSize_t(ival); #endif } static CYTHON_INLINE size_t __Pyx_PyInt_AsSize_t(PyObject* x) { unsigned PY_LONG_LONG val = __Pyx_PyInt_AsUnsignedLongLong(x); if (unlikely(val == (unsigned PY_LONG_LONG)-1 && PyErr_Occurred())) { return (size_t)-1; } else if (unlikely(val != (unsigned PY_LONG_LONG)(size_t)val)) { PyErr_SetString(PyExc_OverflowError, "value too large to convert to size_t"); return (size_t)-1; } return (size_t)val; } #endif /* Py_PYTHON_H */ chaco-4.5.0/chaco/_isnan.h0000644000076600000240000000012612426452312016052 0ustar jrocherstaff00000000000000#ifdef _WIN32 #include #define isnan _isnan #else #include #endif chaco-4.5.0/chaco/_speedups_fallback.py0000644000076600000240000001774612426452312020632 0ustar jrocherstaff00000000000000""" Module that implements pure-python equivalents of the functions in the _speedups extension module. """ from numpy import clip, invert, isnan, isinf, array, transpose, zeros, \ compress, where, take, float32, ones_like import numpy as np import operator def array_combine(a, b, op=operator.and_, func=lambda x: x): """ Returns op(func(a), func(b)) if a and b are both not None; if one is None, then returns func() on the non-None array; if both are None, then returns None. """ if a is not None and b is not None: return op(func(a), func(b)) elif a is not None: return func(a) elif b is not None: return func(b) else: return None def scatterplot_gather_points(index, index_low, index_high, value, value_low, value_high, index_mask=None, index_sel=None, index_sel_mask=None, value_mask=None, value_sel=None, value_sel_mask=None): """ Takes index and value arrays, masks, and optional selection arrays, and returns the list of points and corresponding selection mask for those points. Parameters ---------- index : float array (1D) Array of indexes of the points index_low : float or None The minimum acceptable value in the index array index_high : float or None The maximum acceptable value in the index array value : float array (1D) Array of values of the points value_low : float or None The minimum acceptable value in the value array value_high : float or None The maximum acceptable value in the value array Optional Parameters ------------------- index_mask : bool or int array (1D) Mask array for the indexes index_sel : sequence of ints A list/tuple/array of indices of selected positions in the index array index_sel_mask : array of ints or bools An mask array with True values indicating which points are selected value_mask : bool or int array (1D) Mask array for the values value_sel : sequence of ints A list/tuple/array of indices of selected positions in the value array value_sel_mask : array of ints or bools An mask array with True values indicating which points are selected Returns ------- points : float array (Nx2) The points that match all the masking criteria sel_mask : bool array (1D) Mask indicating which indices in **points** are selected """ index_range_mask = (index_low < index) & (index < index_high) value_range_mask = (value_low < value) & (value < value_high) nan_mask = array_combine(index_mask, value_mask, func = lambda x: invert(isnan(x)) & x) if nan_mask is not None: point_mask = nan_mask & index_range_mask & value_range_mask else: point_mask = index_range_mask & value_range_mask points = transpose(array((index, value))) # Handle the selection mask selection_mask = array_combine(index_sel_mask, value_sel_mask) if index_sel is None and value_sel is None: pass else: if index_sel is not None and value_sel is not None: mask2 = zeros(len(index), int) mask2[index_sel] = 1 mask2[value_sel] &= 1 elif index_sel is not None: mask2 = zeros(len(index), int) mask2[index_sel] = 1 elif value_sel is not None: mask2 = zeros(len(index), int) mask2[value_sel] = 1 if selection_mask is None: selection_mask = mask2 else: selection_mask &= mask2 points = compress(point_mask, points, axis=0) if selection_mask is not None: selections = compress(point_mask, selection_mask) else: selections = None return points, selections def apply_selection_fade(mapped_image, mask, fade_alpha, fade_background): '''Apply a selection fade to a colormapped image. Parameters ---------- mapped_image : ndarray of uint8, shape (N,M,4) The digitized rgba values mask : ndarray of bool, shape (N,M,4) The array of masked pixels fade_alpha : float The alpha value for the fade fade_background : rgb888 tuple The fade background ''' imask = invert(mask) if fade_alpha == 0: mapped_image[imask,0:3] = fade_background else: ialpha = (1.0 - fade_alpha) background = tuple(ialpha * x for x in fade_background) image_region = mapped_image[imask,0:3] image_region *= fade_alpha image_region += background mapped_image[imask,0:3] = image_region def map_colors(data_array, steps, low, high, red_lut, green_lut, blue_lut, alpha_lut): '''Map colors from color lookup tables to a data array. This is used in ColorMapper.map_screen Parameters ---------- data_array : ndarray The data array steps: int The number of steps in the color map (depth) low : float The low end of the data range high : float The high end of the data range red_lut : ndarray of float32 The red channel lookup table green_lut : ndarray of float32 The green channel lookup table blue_lut : ndarray of float32 The blue channel lookup table alpha_lut : ndarray of float32 The alpha channel lookup table Returns ------- rgba: ndarray of float32 The rgba values of data_array according to the lookup tables. The shape of this array is equal to data_array.shape + (4,). ''' range_diff = high - low if range_diff == 0.0 or isinf(range_diff): # Handle null range, or infinite range (which can happen during # initialization before range is connected to a data source). norm_data = 0.5*ones_like(data_array) else: norm_data = clip((data_array - low) / range_diff, 0.0, 1.0) nanmask = isnan(norm_data) norm_data = where(nanmask, 0, (norm_data * (steps-1)).astype(int)) rgba = zeros(norm_data.shape+(4,), float32) rgba[...,0] = where(nanmask, 0, take(red_lut, norm_data)) rgba[...,1] = where(nanmask, 0, take(green_lut, norm_data)) rgba[...,2] = where(nanmask, 0, take(blue_lut, norm_data)) rgba[...,3] = where(nanmask, 0, take(alpha_lut, norm_data)) return rgba def map_colors_uint8(data_array, steps, low, high, red_lut, green_lut, blue_lut, alpha_lut): '''Map colors from color lookup tables to a data array. This is used in ColorMapper.map_screen Parameters ---------- data_array : ndarray The data array steps: int The number of steps in the color map (depth) low : float The low end of the data range high : float The high end of the data range red_lut : ndarray of uint8 The red channel lookup table green_lut : ndarray of uint8 The green channel lookup table blue_lut : ndarray of uint8 The blue channel lookup table alpha_lut : ndarray of uint8 The alpha channel lookup table Returns ------- rgba: ndarray of uint8 The rgba values of data_array according to the lookup tables. The shape of this array is equal to data_array.shape + (4,). ''' range_diff = high - low if range_diff == 0.0 or isinf(range_diff): # Handle null range, or infinite range (which can happen during # initialization before range is connected to a data source). norm_data = 0.5*ones_like(data_array) else: norm_data = clip((data_array - low) / range_diff, 0.0, 1.0) nanmask = isnan(norm_data) norm_data = where(nanmask, 0, (norm_data * (steps-1)).astype('uint8')) rgba = zeros(norm_data.shape+(4,), dtype='uint8') rgba[...,0] = where(nanmask, 0, take(red_lut, norm_data)) rgba[...,1] = where(nanmask, 0, take(green_lut, norm_data)) rgba[...,2] = where(nanmask, 0, take(blue_lut, norm_data)) rgba[...,3] = where(nanmask, 0, take(alpha_lut, norm_data)) return rgba chaco-4.5.0/chaco/abstract_colormap.py0000644000076600000240000000457412426462425020524 0ustar jrocherstaff00000000000000""" Defines the base class for color maps """ from traits.api import Enum, Event, HasTraits, Instance from data_range_1d import DataRange1D class AbstractColormap(HasTraits): """ Abstract class for color maps, which map from scalar values to color values. """ # The data-space bounds of the mapper. range = Instance(DataRange1D) # The color depth of the colors to use. color_depth = Enum('rgba', 'rgb') # A generic "update" event that generally means that anything that relies # on this mapper for visual output should do a redraw or repaint. updated = Event def map_screen(self, val): """ map_screen(val) -> color Maps an array of values to an array of colors. If the input array is NxM, the returned array is NxMx3 or NxMx4, depending on the **color_depth** setting. """ raise NotImplementedError() def map_data(self, ary): """ map_data(ary) -> color_array Returns an array of values containing the colors mapping to the values in *ary*. If the input array is NxM, the returned array is NxMx3 or NxMx4, depending on the **color_depth** setting. """ # XXX this seems bogus: by analogy with AbstractMapper, this should map # colors to data values, and that will be generally hard to do well. # no subclass implements this - CJW raise NotImplementedError() def map_index(self, ary): """ map_index(ary) -> index into color_bands This method is like map_screen(), but it returns an array of indices into the color map's color bands instead of an array of colors. If the input array is NxM, then the output is NxM integer indices. This method might not apply to all color maps. Ones that cannot define a static set of color bands (e.g., function-defined color maps) are not able to implement this function. """ raise NotImplementedError() def map_uint8(self, val): """ map_uint8(val) -> rgb24 or rgba32 color Maps a single value to a single color. Color is represented as either length-3 or length-4 array of rgb(a) uint8 values, depending on the **color_depth** setting. """ # default implementation (not efficient) return (self.map_screen(val)*255.0).astype('uint8') # EOF chaco-4.5.0/chaco/abstract_controller.py0000644000076600000240000000131212426452312021050 0ustar jrocherstaff00000000000000""" Defines the base class for controllers. """ # Enthought library imports from enable.api import Component, Interactor from traits.api import Instance class AbstractController(Interactor): """ Abstract class for tools that manipulate PlotComponents. By default, a controller attaches to a single PlotComponent. """ component = Instance(Component) def __init__(self, component, *args, **kw): self.component = component super(AbstractController, self).__init__(*args, **kw) return def deactivate(self, component): """ This method is called by the component when this controller is no longer the active tool. """ pass # EOF chaco-4.5.0/chaco/abstract_data_range.py0000644000076600000240000001143512426452312020761 0ustar jrocherstaff00000000000000""" Defines the base class for data ranges. """ # Enthought library imports from traits.api import Event, Float, HasTraits, Instance, List, Trait # Local relative imports from abstract_data_source import AbstractDataSource class AbstractDataRange(HasTraits): """ Abstract class for ranges that represent sub-regions of data space. They support "autoscaling" by querying their associated data sources. """ # The list of data sources to which this range responds. sources = List(Instance(AbstractDataSource)) # The actual value of the lower bound of this range. To set it, use # low_setting. (Setting this attribute directly just calls the setter for # low_setting.) Although the default value is specified as 0.0, subclasses # can redefine the default. Also, subclasses can redefined the type to # correspond to their dimensionality. low = Float(0.0) # The actual value of the upper bound of this range. To set it, use # high_setting. (Setting this attribute directly just calls the setter for # high_setting.) Although the default value is specified as 1.0, subclasses # can redefine the default. Also, subclasses can redefined the type to # correspond to their dimensionality. high = Float(1.0) # Setting for the lower bound of this range. low_setting = Trait('auto', 'auto', Float) # Setting for the upper bound of this range. high_setting = Trait('auto', 'auto', Float) # Event that is fired when the actual bounds values change; the value # of the event is a tuple (low_bound, high_bound) updated = Event #------------------------------------------------------------------------ # Concrete methods #------------------------------------------------------------------------ def __init__(self, *sources, **kwargs): if len(sources) > 0: if 'sources' in kwargs: raise RuntimeError("Datasources for data range provided as " "both positional and keyword arguments.") else: kwargs['sources'] = list(sources) super(AbstractDataRange, self).__init__(**kwargs) #------------------------------------------------------------------------ # Abstract methods that subclasses must implement #------------------------------------------------------------------------ def clip_data(self, data): """ Returns a list of data values that are within the range. Given an array of data values of the same dimensionality as the range, returns a list of data values that are inside the range. """ raise NotImplementedError def mask_data(self, data): """ Returns a mask array, indicating whether values in the given array are inside the range. Given an array of data values of the same dimensionality as the range, this method returns a mask array of the same length as data, filled with 1s and 0s corresponding to whether the data value at that index is inside or outside the range. """ raise NotImplementedError def bound_data(self, data): """ Returns a tuple of indices for the start and end of the first run of data that falls within the range. Given an array of data values of the same dimensionality as the range, returns a tuple of indices (start, end) corresponding to the first and last elements of the first run of data that falls within the range. For monotonic data, this basically returns the first and last elements that fall within the range. Using this method is not advised for non-monotonic data; in that case, it returns the first and last elements of the first "chunk" of data that falls within the range. """ raise NotImplementedError def set_bounds(self, *new_bounds): """ Sets all the bounds of the range simultaneously. Because each bounds change probably fires an event, this method allows tools to set all range elements in a single, atomic step. Parameters ---------- new_bounds : a tuple of (low, high) The new bounds for the range; the dimensionality and cardinality depend on the specific subclass. This method not only reduces the number of spurious events (the ones that result from having to set both **high** and **low**), but also allows listeners to differentiate between translation and resize operations. """ raise NotImplementedError def _refresh_bounds(self): """ Resets the values of the bounds depending on the data sources referenced by the range. This method is called only if one of the bounds settings is "auto". """ raise NotImplementedError chaco-4.5.0/chaco/abstract_data_source.py0000644000076600000240000001131512426452312021162 0ustar jrocherstaff00000000000000""" Defines the AbstractDataSource class. """ from traits.api import Bool, Dict, Event, HasTraits # Local relative imports from base import DimensionTrait class AbstractDataSource(HasTraits): """ This abstract interface must be implemented by any class supplying data to Chaco. Chaco does not have a notion of a "data format". For the most part, a data source looks like an array of values with an optional mask and metadata. If you implement this interface, you are responsible for adapting your domain-specific or application-specific data to meet this interface. Chaco provides some basic data source implementations. In most cases, the easiest strategy is to create one of these basic data source with the numeric data from a domain model. In cases when this strategy is not possible, domain classes (or an adapter) must implement AbstractDataSource. """ # The dimensionality of the value at each index point. # Subclasses re-declare this trait as a read-only trait with # the right default value. value_dimension = DimensionTrait # The dimensionality of the indices into this data source. # Subclasses re-declare this trait as a read-only trait with # the right default value. index_dimension = DimensionTrait # A dictionary keyed on strings. In general, it maps to indices (or tuples # of indices, depending on **value_dimension**), as in the case of # selections and annotations. Applications and renderers can add their own # custom metadata, but must avoid using keys that might result in name # collision. metadata = Dict # Event that fires when the data values change. data_changed = Event # Event that fires when just the bounds change. bounds_changed = Event # Event that fires when metadata structure is changed. metadata_changed = Event # Should the data that this datasource refers to be serialized when # the datasource is serialized? persist_data = Bool(True) #------------------------------------------------------------------------ # Abstract methods #------------------------------------------------------------------------ def get_data(self): """get_data() -> data_array Returns a data array of the dimensions of the data source. This data array must not be altered in-place, and the caller must assume it is read-only. This data is contiguous and not masked. In the case of structured (gridded) 2-D data, this method may return two 1-D ArrayDataSources as an optimization. """ raise NotImplementedError def get_data_mask(self): """get_data_mask() -> (data_array, mask_array) Returns the full, raw, source data array and a corresponding binary mask array. Treat both arrays as read-only. The mask is a superposition of the masks of all upstream data sources. The length of the returned array may be much larger than what get_size() returns; the unmasked portion, however, matches what get_size() returns. """ raise NotImplementedError def is_masked(self): """is_masked() -> bool Returns True if this data source's data uses a mask. In this case, to retrieve the data, call get_data_mask() instead of get_data(). If you call get_data() for this data source, it returns data, but that data might not be the expected data. """ raise NotImplementedError def get_size(self): """get_size() -> int Returns an integer estimate or the exact size of the dataset that get_data() returns for this object. This method is useful for down-sampling. """ raise NotImplementedError def get_bounds(self): """get_bounds() -> tuple(min, max) Returns a tuple (min, max) of the bounding values for the data source. In the case of 2-D data, min and max are 2-D points that represent the bounding corners of a rectangle enclosing the data set. Note that these values are not view-dependent, but represent intrinsic properties of the data source. If data is the empty set, then the min and max vals are 0.0. """ raise NotImplementedError ### Persistence ########################################################### def _metadata_default(self): return {"selections":[], "annotations":[]} def __getstate__(self): state = super(AbstractDataSource,self).__getstate__() # everything but 'metadata' for key in ['value_dimension', 'index_dimension', 'persist_data']: if state.has_key(key): del state[key] return state # EOF chaco-4.5.0/chaco/abstract_mapper.py0000644000076600000240000000413112426452312020153 0ustar jrocherstaff00000000000000""" Defines the base class for mappings. """ # Major library imports from numpy import array # Enthought library imports from traits.api import Event, HasTraits, Tuple class AbstractMapper(HasTraits): """ Defines an abstract mapping from a region in input space to a region in output space. """ # A generic "update" event that generally means that anything that relies # on this mapper for visual output should do a redraw or repaint. updated = Event # FIXME: domain_limits is never used # A tuple representing the minimum and maximum values of the domain (data # space). The dimensionality of each value varies depending on the # dimensions of the mapper, so for 1D mappers these will be scalars, for # image and 2D mappers these will be tuples. domain_limits = Tuple(None, None) def map_screen(self, data_array): """ map_screen(data_array) -> screen_array Maps values from data space into screen space. """ return def map_data(self, screen_val): """ map_data(screen_val) -> data_val Maps values from screen space into data space. """ return def map_data_array(self, screen_vals): """ map_data_array(screen_vals) -> data_vals Maps an array of values from screen space into data space. By default, this method just loops over the points, calling map_data() on each one. For vectorizable mapping functions, override this implmentation with a faster one. """ return array([self.map_data(v) for v in screen_vals]) #------------------------------------------------------------------------ # Persistence-related methods #------------------------------------------------------------------------ def __getstate__(self): state = super(AbstractMapper,self).__getstate__() for key in ['_cache_valid']: if state.has_key(key): del state[key] return state def _post_load(self): self._cache_valid = False self._range_changed(None, self.range) return # EOF chaco-4.5.0/chaco/abstract_overlay.py0000644000076600000240000000423612426452312020356 0ustar jrocherstaff00000000000000""" Abstract base class for plot decorators and overlays. This class is primarily used so that tools can easily distinguish between data-related plot items and the decorators on them. """ from enable.api import Component from traits.api import Instance from plot_component import PlotComponent class AbstractOverlay(PlotComponent): """ The base class for overlays and underlays of the plot area. The only default additional feature of an overlay is that it implements an overlay() drawing method that overlays this component on top of another, without the components necessarily having an object containment-ownership relationship. """ # The component that this object overlays. This can be None. By default, if # this object is called to draw(), it tries to render onto this component. component = Instance(Component) # The default layer that this component draws into. draw_layer = "overlay" # The background color (overrides PlotComponent). # Typically, an overlay does not render a background. bgcolor = "transparent" def __init__(self, component=None, *args, **kw): if component is not None: self.component = component super(AbstractOverlay, self).__init__(*args, **kw) def overlay(self, other_component, gc, view_bounds=None, mode="normal"): """ Draws this component overlaid on another component. """ pass def _draw(self, gc, view_bounds=None, mode="normal"): """ Draws the component, paying attention to **draw_order**. If the overlay has a non-null .component, then renders as an overlay; otherwise, default to the standard PlotComponent behavior. Overrides PlotComponent. """ if self.component is not None: self.overlay(self.component, gc, view_bounds, mode) else: super(AbstractOverlay, self)._draw(gc, view_bounds, mode) return def _request_redraw(self): """ Overrides Enable Component. """ if self.component is not None: self.component.request_redraw() super(AbstractOverlay, self)._request_redraw() return # EOF chaco-4.5.0/chaco/abstract_plot_data.py0000644000076600000240000001002212426452312020632 0ustar jrocherstaff00000000000000""" Defines the base class for plot data. """ from traits.api import Bool, Event, HasTraits class AbstractPlotData(HasTraits): """ Defines the interface for data providers to Plot. """ #------------------------------------------------------------------------- # Events that consumers of this data should use #------------------------------------------------------------------------- # Indicates that some of the data has changed. The event object must # be a dict with keys "added", "removed", "changed" and values that are # lists of strings. This event is used by consumers of this data. data_changed = Event #------------------------------------------------------------------------- # Flags - these determine how downstream consumers of the PlotData objet # interact with it. (Typically "consumers" just refers to Plots.) #------------------------------------------------------------------------- # Can consumers (Plots) write data back through this interface using # set_data()? writable = Bool(True) # Can consumers (Plots) set selections? selectable = Bool(True) def list_data(self): """ Returns a list of valid names to use for get_data(). These names are generally strings but can also be integers or any other hashable type. """ raise NotImplementedError def get_data(self, name): """ Returns the data or data source associated with *name*. If there is no data or data source associated with the name, this method returns None. """ raise NotImplementedError def del_data(self, name): """ Deletes the array specified by *name*, or raises a KeyError if the named array does not exist. If the instance is not writable, then this must do nothing. """ raise NotImplementedError def set_data(self, name, new_data, generate_name=False): """ Sets the specified array as the value for either the specified name or a generated name. If the instance's `writable` attribute is True, then this method sets the data associated with the given name to the new value, otherwise it does nothing. Parameters ---------- name : string The name of the array whose value is to be set. new_data : array The array to set as the value of *name*. generate_name : Boolean If True, a unique name of the form 'seriesN' is created for the array, and is used in place of *name*. The 'N' in 'seriesN' is one greater the largest N already used. Returns ------- The name under which the array was set. """ raise NotImplementedError def update_data(self, *args, **kwargs): """ Update a set of data values, firing only one data_changed event. This function has the same signature as the dictionary update() method. """ raise NotImplementedError def set_selection(self, name, selection): """ Sets the selection on the specified data. This method informs the class that Chaco has selected a portion of the data. Parameters ---------- name : string Name of an array selection : array of Booleans Indicates whether the data in the cooresponding position of the array named by *name* is selected. """ raise NotImplementedError #------------------------------------------------------------------------ # Dictionary Interface #------------------------------------------------------------------------ def __getitem__(self, name): return self.arrays.get(name, None) def __setitem__(self, name, value): return self.set_data(name, value) def __delitem__(self, name): return self.del_data(name) def update(self, *args, **kwargs): self.update_data(*args, **kwargs) chaco-4.5.0/chaco/abstract_plot_renderer.py0000644000076600000240000000606212426452312021540 0ustar jrocherstaff00000000000000""" Defines a base class for plot renderers. """ # Enthought library imports. from traits.api import Enum # Local relative imports from plot_component import PlotComponent class AbstractPlotRenderer(PlotComponent): """ This is the minimal interface that all plot renderers must support. Higher-dimensionality plot renderers can implement a richer subclass of this abstract class. This interface exists mostly to support the development of generic interactors and plot tools. """ origin = Enum("bottom left", "top left", "bottom right", "top right") #------------------------------------------------------------------------ # Override default values of inherited traits PlotComponent #------------------------------------------------------------------------ # Overrides the default value inherited from PlotComponent. bgcolor = "transparent" # Overrides the default value inherited from PlotComponent. resizable = "hv" def map_screen(self, data_array): """ Maps an array of data points to screen space and returns an array of screen space points. """ raise NotImplementedError def map_data(self, screen_pt): """ Maps a screen space point (sx, sy) to the "index" space of the plot. Returns a floating point number, *not* an integer index. """ raise NotImplementedError def map_index(self, screen_pt, threshold=0.0, outside_returns_none=True, \ index_only = False): """ Maps a screen space point to an index into the plot's index array(s). Parameters ---------- screen_pt : (x,y) The screen space point to map. threshold : float Optional screen-space distance allowed between *screen_pt* and the plot; if non-zero, then a *screen_pt* within this distance is mapped to the neared plot index. (This feature is useful for sparse 2-D data.) outside_returns_none : Boolean If True, then if *screen_pt* is outside the range of the data, the method returns None. If False, it returns the nearest end index in such a case. index_only : Boolean If True, then this method maps based only on the index coordinate of *screen_pt*, and ignores the value coordinate. Returns ------- An index into the plot's index array(s). Typically this index is just an integer, but if the plot has a 2-D index dimension, then this method returns a tuple of integers. If the input point cannot be mapped to an index, then None is returned. If *screen_pt* corresponds to multiple indices, then only the first index is returned. """ raise NotImplementedError def _render_icon(self, gc, x, y, width, height): """ Renders an icon for this plot. This method is used by the legend to draw representation of this plot as an icon into the box defined by the given coordinates. """ pass # EOF chaco-4.5.0/chaco/api.py0000644000076600000240000001006712426462425015570 0ustar jrocherstaff00000000000000""" Defines the publicly accessible items of the Chaco API. """ # This just imports the key datamodel classes into the top-level package # namespace for convenience. from base import NumericalSequenceTrait, PointTrait, ImageTrait, DimensionTrait, \ SortOrderTrait, bin_search, reverse_map_1d, right_shift, \ left_shift, sort_points, find_runs, arg_find_runs, \ point_line_distance # Data model from abstract_data_source import AbstractDataSource from array_data_source import ArrayDataSource from grid_data_source import GridDataSource from image_data import ImageData from multi_array_data_source import MultiArrayDataSource from point_data_source import PointDataSource from abstract_data_range import AbstractDataRange from base_data_range import BaseDataRange from data_range_1d import DataRange1D from data_range_2d import DataRange2D # Mappers from abstract_mapper import AbstractMapper from base_1d_mapper import Base1DMapper from grid_mapper import GridMapper from log_mapper import LogMapper from linear_mapper import LinearMapper from color_mapper import ColorMapper, ColorMapTemplate from transform_color_mapper import TransformColorMapper # Colormaps and color palettes from default_colormaps import * from default_colors import * # Visual components from abstract_plot_renderer import AbstractPlotRenderer from abstract_overlay import AbstractOverlay from base_plot_container import BasePlotContainer from base_plot_frame import BasePlotFrame from cross_plot_frame import CrossPlotFrame from data_view import DataView from simple_plot_frame import SimplePlotFrame from plot_component import PlotComponent from plot_graphics_context import PlotGraphicsContext, PlotGraphicsContextMixin from selectable_overlay_container import SelectableOverlayPlotContainer from plot_containers import OverlayPlotContainer, HPlotContainer, VPlotContainer, \ GridPlotContainer GridContainer = GridPlotContainer from label import Label from plot_label import PlotLabel from legend import Legend from tooltip import ToolTip from data_label import DataLabel from lasso_overlay import LassoOverlay from color_bar import ColorBar from text_box_overlay import TextBoxOverlay from scatter_inspector_overlay import ScatterInspectorOverlay # Renderers from barplot import BarPlot from base_2d_plot import Base2DPlot from base_xy_plot import BaseXYPlot from scatterplot import ScatterPlot, render_markers from image_plot import ImagePlot from cmap_image_plot import CMapImagePlot from contour_line_plot import ContourLinePlot from contour_poly_plot import ContourPolyPlot from lineplot import LinePlot from colormapped_scatterplot import ColormappedScatterPlot from colormapped_selection_overlay import ColormappedSelectionOverlay from polygon_plot import PolygonPlot from errorbar_plot import ErrorBarPlot from filled_line_plot import FilledLinePlot from quiverplot import QuiverPlot from candle_plot import CandlePlot from multi_line_plot import MultiLinePlot from jitterplot import JitterPlot from variable_size_scatterplot import VariableSizeScatterPlot from horizon_plot import BandedMapper, HorizonPlot # Plot factories from plot_factory import create_bar_plot, create_line_plot, create_scatter_plot, \ create_polar_plot, add_default_axes, add_default_grids from abstract_plot_data import AbstractPlotData from array_plot_data import ArrayPlotData from plot import Plot from toolbar_plot import ToolbarPlot # Axis from axis import PlotAxis from label_axis import LabelAxis from ticks import AbstractTickGenerator, DefaultTickGenerator, auto_ticks, auto_interval, \ tick_intervals, log_auto_ticks, auto_bounds, calc_bound # Grid from grid import PlotGrid # Style stuff #from stylable import Stylable #from stylesheets import Style, StyleSheet # Tools from abstract_controller import AbstractController # Importing various symbols into the Chaco namespace for backwards # compatibility. New code should directly import from Enable. from enable.base_tool import BaseTool, KeySpec from enable.markers import marker_trait #EOF chaco-4.5.0/chaco/array_data_source.py0000644000076600000240000002437412426462410020506 0ustar jrocherstaff00000000000000""" Defines the ArrayDataSource class.""" # Major library imports from numpy import array, isfinite, ones, ndarray import numpy as np # Enthought library imports from traits.api import Any, Constant, Int, Tuple # Chaco imports from base import NumericalSequenceTrait, reverse_map_1d, SortOrderTrait from abstract_data_source import AbstractDataSource def bounded_nanargmin(arr): """ Find the index of the minimum value, ignoring NaNs. If all NaNs, return 0. """ # Different versions of numpy behave differently in the all-NaN case, so we # catch this condition in two different ways. try: if np.issubdtype(arr.dtype, np.floating): min = np.nanargmin(arr) elif np.issubdtype(arr.dtype, np.number): min = np.argmin(arr) else: min = 0 except ValueError: return 0 if isfinite(min): return min else: return 0 def bounded_nanargmax(arr): """ Find the index of the maximum value, ignoring NaNs. If all NaNs, return -1. """ try: if np.issubdtype(arr.dtype, np.floating): max = np.nanargmax(arr) elif np.issubdtype(arr.dtype, np.number): max = np.argmax(arr) else: max = -1 except ValueError: return -1 if isfinite(max): return max else: return -1 class ArrayDataSource(AbstractDataSource): """ A data source representing a single, continuous array of numerical data. This class does not listen to the array for value changes; if you need that behavior, create a subclass that hooks up the appropriate listeners. """ #------------------------------------------------------------------------ # AbstractDataSource traits #------------------------------------------------------------------------ # The dimensionality of the indices into this data source (overrides # AbstractDataSource). index_dimension = Constant('scalar') # The dimensionality of the value at each index point (overrides # AbstractDataSource). value_dimension = Constant('scalar') # The sort order of the data. # This is a specialized optimization for 1-D arrays, but it's an important # one that's used everywhere. sort_order = SortOrderTrait #------------------------------------------------------------------------ # Private traits #------------------------------------------------------------------------ # The data array itself. _data = NumericalSequenceTrait # Cached values of min and max as long as **_data** doesn't change. _cached_bounds = Tuple # Not necessary, since this is not a filter, but provided for convenience. _cached_mask = Any # The index of the (first) minimum value in self._data # FIXME: This is an Any instead of an Int trait because of how Traits # typechecks numpy.int64 on 64-bit Windows systems. _min_index = Any # The index of the (first) maximum value in self._data # FIXME: This is an Any instead of an Int trait because of how Traits # typechecks numpy.int64 on 64-bit Windows systems. _max_index = Any #------------------------------------------------------------------------ # Public methods #------------------------------------------------------------------------ def __init__(self, data=array([]), sort_order="none", **kw): AbstractDataSource.__init__(self, **kw) self.set_data(data, sort_order) return def set_data(self, newdata, sort_order=None): """ Sets the data, and optionally the sort order, for this data source. Parameters ---------- newdata : array The data to use. sort_order : SortOrderTrait The sort order of the data """ self._data = newdata if sort_order is not None: self.sort_order = sort_order self._compute_bounds() self.data_changed = True return def set_mask(self, mask): """ Sets the mask for this data source. """ self._cached_mask = mask self.data_changed = True return def remove_mask(self): """ Removes the mask on this data source. """ self._cached_mask = None self.data_changed = True return #------------------------------------------------------------------------ # AbstractDataSource interface #------------------------------------------------------------------------ def get_data(self): """ Returns the data for this data source, or 0.0 if it has no data. Implements AbstractDataSource. """ if self._data is not None: return self._data else: return 0.0 def get_data_mask(self): """get_data_mask() -> (data_array, mask_array) Implements AbstractDataSource. """ if self._cached_mask is None: return self._data, ones(len(self._data), dtype=bool) else: return self._data, self._cached_mask def is_masked(self): """is_masked() -> bool Implements AbstractDataSource. """ if self._cached_mask is not None: return True else: return False def get_size(self): """get_size() -> int Implements AbstractDataSource. """ if self._data is not None: return len(self._data) else: return 0 def get_bounds(self): """ Returns the minimum and maximum values of the data source's data. Implements AbstractDataSource. """ if self._cached_bounds is None or self._cached_bounds == () or \ self._cached_bounds == 0.0: self._compute_bounds() return self._cached_bounds def reverse_map(self, pt, index=0, outside_returns_none=True): """Returns the index of *pt* in the data source. Parameters ---------- pt : scalar value value to find index ignored for data series with 1-D indices outside_returns_none : Boolean Whether the method returns None if *pt* is outside the range of the data source; if False, the method returns the value of the bound that *pt* is outside of. """ if self.sort_order == "none": raise NotImplementedError # index is ignored for dataseries with 1-dimensional indices minval, maxval = self._cached_bounds if (pt < minval): if outside_returns_none: return None else: return self._min_index elif (pt > maxval): if outside_returns_none: return None else: return self._max_index else: return reverse_map_1d(self._data, pt, self.sort_order) #------------------------------------------------------------------------ # Private methods #------------------------------------------------------------------------ def _compute_bounds(self, data=None): """ Computes the minimum and maximum values of self._data. If a data array is passed in, then that is used instead of self._data. This behavior is useful for subclasses. """ # TODO: as an optimization, perhaps create and cache a sorted # version of the dataset? if data is None: # Several sources weren't setting the _data attribute, so we # go through the interface. This seems like the correct thing # to do anyway... right? #data = self._data data = self.get_data() data_len = 0 try: data_len = len(data) except: pass if data_len == 0: self._min_index = 0 self._max_index = 0 self._cached_bounds = (0.0, 0.0) elif data_len == 1: self._min_index = 0 self._max_index = 0 self._cached_bounds = (data[0], data[0]) else: if self.sort_order == "ascending": self._min_index = 0 self._max_index = -1 elif self.sort_order == "descending": self._min_index = -1 self._max_index = 0 else: # ignore NaN values. This is probably a little slower, # but also much safer. # data might be an array of strings or objects that # can't have argmin calculated on them. try: # the data may be in a subclass of numpy.array, viewing # the data as a ndarray will remove side effects of # the subclasses, such as different operator behaviors self._min_index = bounded_nanargmin(data.view(ndarray)) self._max_index = bounded_nanargmax(data.view(ndarray)) except (TypeError, IndexError, NotImplementedError): # For strings and objects, we punt... These show up in # label-ish data sources. self._cached_bounds = (0.0, 0.0) self._cached_bounds = (data[self._min_index], data[self._max_index]) return #------------------------------------------------------------------------ # Event handlers #------------------------------------------------------------------------ def _metadata_changed(self, event): self.metadata_changed = True def _metadata_items_changed(self, event): self.metadata_changed = True #------------------------------------------------------------------------ # Persistence-related methods #------------------------------------------------------------------------ def __getstate__(self): state = self.__dict__.copy() if not self.persist_data: state.pop("_data", None) state.pop("_cached_mask", None) state.pop("_cached_bounds", None) state.pop("_min_index", None) state.pop("_max_index", None) return state def _post_load(self): super(ArrayDataSource, self)._post_load() self._cached_bounds = () self._cached_mask = None return # EOF chaco-4.5.0/chaco/array_plot_data.py0000644000076600000240000001465412426452312020164 0ustar jrocherstaff00000000000000""" Defines ArrayPlotData. """ from numpy import array, ndarray # Enthought library imports from traits.api import Dict # Local, relative imports from .abstract_plot_data import AbstractPlotData from .abstract_data_source import AbstractDataSource class ArrayPlotData(AbstractPlotData): """ A PlotData implementation class that handles a list of Numpy arrays (or a 2-D Numpy array). By default, it doesn't allow its input data to be modified by downstream Chaco components or interactors. """ #------------------------------------------------------------------------- # Public traits #------------------------------------------------------------------------- # Map of names to arrays. Although there is no restriction on the array # dimensions, each array must correspond to a single plot item; that # is, a single name must not map to a multi-dimensional array unless # the array is being used for an image plot or for something that can handle # multi-dimensional input data. arrays = Dict # Consumers can write data to this object (overrides AbstractPlotData). writable = True def __init__(self, *data, **kw): """ ArrayPlotData can be constructed by passing in arrays. Keyword arguments can be used to give certain arrays specific names; unnamed arrays are given a generic name of the format 'seriesN', where N is its position in the argument list. For example:: ArrayPlotData(array1, array2, index=array3, foo=array4) This call results in the creation of four entries in self.arrays:: 'series1' -> array1 'series2' -> array2 'index' -> array3 'foo' -> array4 If any names in the keyword parameter list collide with the auto-generated positional names "series1", "series2", etc., then those arrays are replaced. Note that this factor means that keyword traits are *not* set using the keyword parameters in the constructor. This strategy defies some conventions, but was it chosen for convenience, since the raison d'etre of this class is convenience. """ super(AbstractPlotData, self).__init__() self._update_data(kw) data = dict(zip(self._generate_names(len(data)), data)) self._update_data(data) #------------------------------------------------------------------------ # AbstractPlotData Interface #------------------------------------------------------------------------ def list_data(self): """ Returns a list of the names of the arrays managed by this instance. """ return self.arrays.keys() def get_data(self, name): """ Returns the array associated with *name*. Implements AbstractDataSource. """ return self.arrays.get(name, None) def del_data(self, name): """ Deletes the array specified by *name*, or raises a KeyError if the named array does not exist. """ if not self.writable: return None if name in self.arrays: del self.arrays[name] self.data_changed = {'removed': [name]} else: raise KeyError("Data series '%s' does not exist." % name) def set_data(self, name, new_data, generate_name=False): """ Sets the specified array as the value for either the specified name or a generated name. If the instance's `writable` attribute is True, then this method sets the data associated with the given name to the new value, otherwise it does nothing. Parameters ---------- name : string The name of the array whose value is to be set. new_data : array The array to set as the value of *name*. generate_name : Boolean If True, a unique name of the form 'seriesN' is created for the array, and is used in place of *name*. The 'N' in 'seriesN' is one greater the largest N already used. Returns ------- The name under which the array was set. """ if not self.writable: return None if generate_name: names = self._generate_names(1) name = names[0] self.update_data({name: new_data}) return name def update_data(self, *args, **kwargs): """ Sets the specified array as the value for either the specified name or a generated name. Implements AbstractPlotData's update_data() method. This method has the same signature as the dictionary update() method. """ if not self.writable: return None data = dict(*args, **kwargs) event = {} for name in data: if name in self.arrays: event.setdefault('changed', []).append(name) else: event.setdefault('added', []).append(name) self._update_data(data) self.data_changed = event def set_selection(self, name, selection): """ Overrides AbstractPlotData to do nothing and not raise an error. """ pass #------------------------------------------------------------------------ # Private methods #------------------------------------------------------------------------ def _generate_names(self, n): """ Generate n new names """ max_index = max(self._generate_indices()) names = ["series{0:d}".format(n) for n in range(max_index+1, max_index+n+1)] return names def _generate_indices(self): """ Generator that yields all integers that match "series%d" in keys """ yield 0 # default minimum for name in self.list_data(): if name.startswith('series'): try: v = int(name[6:]) except ValueError: continue yield v def _update_data(self, data): """ Update the array, ensuring that data is an array """ # note that this call modifies data, but that's OK since the callers # all create the dictionary that they pass in for name, value in data.items(): if not isinstance(value, (ndarray, AbstractDataSource)): data[name] = array(value) else: data[name] = value self.arrays.update(data) chaco-4.5.0/chaco/axis.py0000644000076600000240000007236512426452312015766 0ustar jrocherstaff00000000000000""" Defines the PlotAxis class, and associated validator and UI. """ from __future__ import with_statement # Major library import from numpy import array, around, absolute, cos, dot, float64, inf, pi, \ sqrt, sin, transpose # Enthought Library imports from enable.api import ColorTrait, LineStyle from kiva.trait_defs.kiva_font_trait import KivaFont from traits.api import Any, Float, Int, Str, Trait, Unicode, \ Bool, Event, List, Array, Instance, Enum, Callable # Local relative imports from ticks import AbstractTickGenerator, DefaultTickGenerator from abstract_mapper import AbstractMapper from abstract_overlay import AbstractOverlay from label import Label from log_mapper import LogMapper def DEFAULT_TICK_FORMATTER(val): return ("%f"%val).rstrip("0").rstrip(".") class PlotAxis(AbstractOverlay): """ The PlotAxis is a visual component that can be rendered on its own as a standalone component or attached as an overlay to another component. (To attach it as an overlay, set its **component** attribute.) When it is attached as an overlay, it draws into the padding around the component. """ # The mapper that drives this axis. mapper = Instance(AbstractMapper) # Keep an origin for plots that aren't attached to a component origin = Enum("bottom left", "top left", "bottom right", "top right") # The text of the axis title. title = Trait('', Str, Unicode) #May want to add PlotLabel option # The font of the title. title_font = KivaFont('modern 12') # The spacing between the axis line and the title title_spacing = Trait('auto', 'auto', Float) # The color of the title. title_color = ColorTrait("black") # The thickness (in pixels) of each tick. tick_weight = Float(1.0) # The color of the ticks. tick_color = ColorTrait("black") # The font of the tick labels. tick_label_font = KivaFont('modern 10') # The color of the tick labels. tick_label_color = ColorTrait("black") # The rotation of the tick labels. tick_label_rotate_angle = Float(0) # Whether to align to corners or edges (corner is better for 45 degree rotation) tick_label_alignment = Enum('edge', 'corner') # The margin around the tick labels. tick_label_margin = Int(2) # The distance of the tick label from the axis. tick_label_offset = Float(8.) # Whether the tick labels appear to the inside or the outside of the plot area tick_label_position = Enum("outside", "inside") # A callable that is passed the numerical value of each tick label and # that returns a string. tick_label_formatter = Callable(DEFAULT_TICK_FORMATTER) # The number of pixels by which the ticks extend into the plot area. tick_in = Int(5) # The number of pixels by which the ticks extend into the label area. tick_out = Int(5) # Are ticks visible at all? tick_visible = Bool(True) # The dataspace interval between ticks. tick_interval = Trait('auto', 'auto', Float) # A callable that implements the AbstractTickGenerator interface. tick_generator = Instance(AbstractTickGenerator) # The location of the axis relative to the plot. This determines where # the axis title is located relative to the axis line. orientation = Enum("top", "bottom", "left", "right") # Is the axis line visible? axis_line_visible = Bool(True) # The color of the axis line. axis_line_color = ColorTrait("black") # The line thickness (in pixels) of the axis line. axis_line_weight = Float(1.0) # The dash style of the axis line. axis_line_style = LineStyle('solid') # A special version of the axis line that is more useful for geophysical # plots. small_haxis_style = Bool(False) # Does the axis ensure that its end labels fall within its bounding area? ensure_labels_bounded = Bool(False) # Does the axis prevent the ticks from being rendered outside its bounds? # This flag is off by default because the standard axis *does* render ticks # that encroach on the plot area. ensure_ticks_bounded = Bool(False) # Fired when the axis's range bounds change. updated = Event #------------------------------------------------------------------------ # Override default values of inherited traits #------------------------------------------------------------------------ # Background color (overrides AbstractOverlay). Axes usually let the color of # the container show through. bgcolor = ColorTrait("transparent") # Dimensions that the axis is resizable in (overrides PlotComponent). # Typically, axes are resizable in both dimensions. resizable = "hv" #------------------------------------------------------------------------ # Private Traits #------------------------------------------------------------------------ # Cached position calculations _tick_list = List # These are caches of their respective positions _tick_positions = Any #List _tick_label_list = Any _tick_label_positions = Any _tick_label_bounding_boxes = List _major_axis_size = Float _minor_axis_size = Float _major_axis = Array _title_orientation = Array _title_angle = Float _origin_point = Array _inside_vector = Array _axis_vector = Array _axis_pixel_vector = Array _end_axis_point = Array ticklabel_cache = List _cache_valid = Bool(False) #------------------------------------------------------------------------ # Public methods #------------------------------------------------------------------------ def __init__(self, component=None, **kwargs): # TODO: change this back to a factory in the instance trait some day self.tick_generator = DefaultTickGenerator() # Override init so that our component gets set last. We want the # _component_changed() event handler to get run last. super(PlotAxis, self).__init__(**kwargs) if component is not None: self.component = component def invalidate(self): """ Invalidates the pre-computed layout and scaling data. """ self._reset_cache() self.invalidate_draw() return def traits_view(self): """ Returns a View instance for use with Traits UI. This method is called automatically be the Traits framework when .edit_traits() is invoked. """ from axis_view import AxisView return AxisView #------------------------------------------------------------------------ # PlotComponent and AbstractOverlay interface #------------------------------------------------------------------------ def _do_layout(self, *args, **kw): """ Tells this component to do layout at a given size. Overrides Component. """ if self.use_draw_order and self.component is not None: self._layout_as_overlay(*args, **kw) else: super(PlotAxis, self)._do_layout(*args, **kw) return def overlay(self, component, gc, view_bounds=None, mode='normal'): """ Draws this component overlaid on another component. Overrides AbstractOverlay. """ if not self.visible: return self._draw_component(gc, view_bounds, mode, component) return def _draw_overlay(self, gc, view_bounds=None, mode='normal'): """ Draws the overlay layer of a component. Overrides PlotComponent. """ self._draw_component(gc, view_bounds, mode) return def _draw_component(self, gc, view_bounds=None, mode='normal', component=None): """ Draws the component. This method is preserved for backwards compatibility. Overrides PlotComponent. """ if not self.visible: return if not self._cache_valid: if component is not None: self._calculate_geometry_overlay(component) else: self._calculate_geometry() self._compute_tick_positions(gc, component) self._compute_labels(gc) with gc: # slight optimization: if we set the font correctly on the # base gc before handing it in to our title and tick labels, # their set_font() won't have to do any work. gc.set_font(self.tick_label_font) if self.axis_line_visible: self._draw_axis_line(gc, self._origin_point, self._end_axis_point) if self.title: self._draw_title(gc) self._draw_ticks(gc) self._draw_labels(gc) self._cache_valid = True return #------------------------------------------------------------------------ # Private draw routines #------------------------------------------------------------------------ def _layout_as_overlay(self, size=None, force=False): """ Lays out the axis as an overlay on another component. """ if self.component is not None: if self.orientation in ("left", "right"): self.y = self.component.y self.height = self.component.height if self.orientation == "left": self.width = self.component.padding_left self.x = self.component.outer_x elif self.orientation == "right": self.width = self.component.padding_right self.x = self.component.x2 + 1 else: self.x = self.component.x self.width = self.component.width if self.orientation == "bottom": self.height = self.component.padding_bottom self.y = self.component.outer_y elif self.orientation == "top": self.height = self.component.padding_top self.y = self.component.y2 + 1 return def _draw_axis_line(self, gc, startpoint, endpoint): """ Draws the line for the axis. """ with gc: gc.set_antialias(0) gc.set_line_width(self.axis_line_weight) gc.set_stroke_color(self.axis_line_color_) gc.set_line_dash(self.axis_line_style_) gc.move_to(*around(startpoint)) gc.line_to(*around(endpoint)) gc.stroke_path() return def _draw_title(self, gc, label=None, axis_offset=None): """ Draws the title for the axis. """ if label is None: title_label = Label(text=self.title, font=self.title_font, color=self.title_color, rotate_angle=self.title_angle) else: title_label = label # get the _rotated_ bounding box of the label tl_bounds = array(title_label.get_bounding_box(gc), float64) text_center_to_corner = -tl_bounds/2.0 # which axis are we moving away from the axis line along? axis_index = self._major_axis.argmin() if self.title_spacing != 'auto': axis_offset = self.title_spacing if (self.title_spacing) and (axis_offset is None ): if not self.ticklabel_cache: axis_offset = 25 else: axis_offset = max([l._bounding_box[axis_index] for l in self.ticklabel_cache]) * 1.3 offset = (self._origin_point+self._end_axis_point)/2 axis_dist = self.tick_out + tl_bounds[axis_index]/2.0 + axis_offset offset -= self._inside_vector * axis_dist offset += text_center_to_corner gc.translate_ctm(*offset) title_label.draw(gc) gc.translate_ctm(*(-offset)) return def _draw_ticks(self, gc): """ Draws the tick marks for the axis. """ if not self.tick_visible: return gc.set_stroke_color(self.tick_color_) gc.set_line_width(self.tick_weight) gc.set_antialias(False) gc.begin_path() tick_in_vector = self._inside_vector*self.tick_in tick_out_vector = self._inside_vector*self.tick_out for tick_pos in self._tick_positions: gc.move_to(*(tick_pos + tick_in_vector)) gc.line_to(*(tick_pos - tick_out_vector)) gc.stroke_path() return def _draw_labels(self, gc): """ Draws the tick labels for the axis. """ # which axis are we moving away from the axis line along? axis_index = self._major_axis.argmin() inside_vector = self._inside_vector if self.tick_label_position == "inside": inside_vector = -inside_vector for i in range(len(self._tick_label_positions)): #We want a more sophisticated scheme than just 2 decimals all the time ticklabel = self.ticklabel_cache[i] tl_bounds = self._tick_label_bounding_boxes[i] #base_position puts the tick label at a point where the vector #extending from the tick mark inside 8 units #just touches the rectangular bounding box of the tick label. #Note: This is not necessarily optimal for non #horizontal/vertical axes. More work could be done on this. base_position = self._tick_label_positions[i].copy() axis_dist = self.tick_label_offset + tl_bounds[axis_index]/2.0 base_position -= inside_vector * axis_dist base_position -= tl_bounds/2.0 if self.tick_label_alignment == 'corner': if self.orientation in ("top", "bottom"): base_position[0] += tl_bounds[0]/2.0 elif self.orientation == "left": base_position[1] -= tl_bounds[1]/2.0 elif self.orientation == "right": base_position[1] += tl_bounds[1]/2.0 if self.ensure_labels_bounded: bound_idx = self._major_axis.argmax() if i == 0: base_position[bound_idx] = max(base_position[bound_idx], self._origin_point[bound_idx]) elif i == len(self._tick_label_positions)-1: base_position[bound_idx] = min(base_position[bound_idx], self._end_axis_point[bound_idx] - \ tl_bounds[bound_idx]) tlpos = around(base_position) gc.translate_ctm(*tlpos) ticklabel.draw(gc) gc.translate_ctm(*(-tlpos)) return #------------------------------------------------------------------------ # Private methods for computing positions and layout #------------------------------------------------------------------------ def _reset_cache(self): """ Clears the cached tick positions, labels, and label positions. """ self._tick_positions = [] self._tick_label_list = [] self._tick_label_positions = [] return def _compute_tick_positions(self, gc, overlay_component=None): """ Calculates the positions for the tick marks. """ if (self.mapper is None): self._reset_cache() self._cache_valid = True return datalow = self.mapper.range.low datahigh = self.mapper.range.high screenhigh = self.mapper.high_pos screenlow = self.mapper.low_pos if overlay_component is not None: origin = getattr(overlay_component, 'origin', 'bottom left') else: origin = self.origin if self.orientation in ("top", "bottom"): if "right" in origin: flip_from_gc = True else: flip_from_gc = False elif self.orientation in ("left", "right"): if "top" in origin: flip_from_gc = True else: flip_from_gc = False if flip_from_gc: screenlow, screenhigh = screenhigh, screenlow if (datalow == datahigh) or (screenlow == screenhigh) or \ (datalow in [inf, -inf]) or (datahigh in [inf, -inf]): self._reset_cache() self._cache_valid = True return if datalow > datahigh: raise RuntimeError, "DataRange low is greater than high; unable to compute axis ticks." if not self.tick_generator: return if hasattr(self.tick_generator, "get_ticks_and_labels"): # generate ticks and labels simultaneously tmp = self.tick_generator.get_ticks_and_labels(datalow, datahigh, screenlow, screenhigh) if len(tmp) == 0: tick_list = [] labels = [] else: tick_list, labels = tmp # compute the labels here self.ticklabel_cache = [Label(text=lab, font=self.tick_label_font, color=self.tick_label_color) \ for lab in labels] self._tick_label_bounding_boxes = [array(ticklabel.get_bounding_box(gc), float64) \ for ticklabel in self.ticklabel_cache] else: scale = 'log' if isinstance(self.mapper, LogMapper) else 'linear' if self.small_haxis_style: tick_list = array([datalow, datahigh]) else: tick_list = array(self.tick_generator.get_ticks(datalow, datahigh, datalow, datahigh, self.tick_interval, use_endpoints=False, scale=scale), float64) mapped_tick_positions = (array(self.mapper.map_screen(tick_list))-screenlow) / \ (screenhigh-screenlow) self._tick_positions = around(array([self._axis_vector*tickpos + self._origin_point \ for tickpos in mapped_tick_positions])) self._tick_label_list = tick_list self._tick_label_positions = self._tick_positions return def _compute_labels(self, gc): """Generates the labels for tick marks. Waits for the cache to become invalid. """ # tick labels are already computed if hasattr(self.tick_generator, "get_ticks_and_labels"): return formatter = self.tick_label_formatter def build_label(val): tickstring = formatter(val) if formatter is not None else str(val) return Label(text=tickstring, font=self.tick_label_font, color=self.tick_label_color, rotate_angle=self.tick_label_rotate_angle, margin=self.tick_label_margin) self.ticklabel_cache = [build_label(val) for val in self._tick_label_list] self._tick_label_bounding_boxes = [array(ticklabel.get_bounding_box(gc), float) for ticklabel in self.ticklabel_cache] return def _calculate_geometry(self): origin = self.origin screenhigh = self.mapper.high_pos screenlow = self.mapper.low_pos if self.orientation in ('top', 'bottom'): self._major_axis_size = self.bounds[0] self._minor_axis_size = self.bounds[1] self._major_axis = array([1., 0.]) self._title_orientation = array([0.,1.]) self.title_angle = 0.0 if self.orientation == 'top': self._origin_point = array(self.position) self._inside_vector = array([0.,-1.]) else: #self.oriention == 'bottom' self._origin_point = array(self.position) + array([0., self.bounds[1]]) self._inside_vector = array([0., 1.]) if "right" in origin: screenlow, screenhigh = screenhigh, screenlow elif self.orientation in ('left', 'right'): self._major_axis_size = self.bounds[1] self._minor_axis_size = self.bounds[0] self._major_axis = array([0., 1.]) self._title_orientation = array([-1., 0]) if self.orientation == 'left': self._origin_point = array(self.position) + array([self.bounds[0], 0.]) self._inside_vector = array([1., 0.]) self.title_angle = 90.0 else: #self.orientation == 'right' self._origin_point = array(self.position) self._inside_vector = array([-1., 0.]) self.title_angle = 270.0 if "top" in origin: screenlow, screenhigh = screenhigh, screenlow if self.ensure_ticks_bounded: self._origin_point -= self._inside_vector*self.tick_in self._end_axis_point = abs(screenhigh-screenlow)*self._major_axis + self._origin_point self._axis_vector = self._end_axis_point - self._origin_point # This is the vector that represents one unit of data space in terms of screen space. self._axis_pixel_vector = self._axis_vector/sqrt(dot(self._axis_vector,self._axis_vector)) return def _calculate_geometry_overlay(self, overlay_component=None): if overlay_component is None: overlay_component = self component_origin = getattr(overlay_component, "origin", 'bottom left') screenhigh = self.mapper.high_pos screenlow = self.mapper.low_pos if self.orientation in ('top', 'bottom'): self._major_axis_size = overlay_component.bounds[0] self._minor_axis_size = overlay_component.bounds[1] self._major_axis = array([1., 0.]) self._title_orientation = array([0.,1.]) self.title_angle = 0.0 if self.orientation == 'top': self._origin_point = array([overlay_component.x, overlay_component.y2]) self._inside_vector = array([0.0, -1.0]) else: self._origin_point = array([overlay_component.x, overlay_component.y]) self._inside_vector = array([0.0, 1.0]) if "right" in component_origin: screenlow, screenhigh = screenhigh, screenlow elif self.orientation in ('left', 'right'): self._major_axis_size = overlay_component.bounds[1] self._minor_axis_size = overlay_component.bounds[0] self._major_axis = array([0., 1.]) self._title_orientation = array([-1., 0]) if self.orientation == 'left': self._origin_point = array([overlay_component.x, overlay_component.y]) self._inside_vector = array([1.0, 0.0]) self.title_angle = 90.0 else: self._origin_point = array([overlay_component.x2, overlay_component.y]) self._inside_vector = array([-1.0, 0.0]) self.title_angle = 270.0 if "top" in component_origin: screenlow, screenhigh = screenhigh, screenlow if self.ensure_ticks_bounded: self._origin_point -= self._inside_vector*self.tick_in self._end_axis_point = abs(screenhigh-screenlow)*self._major_axis + self._origin_point self._axis_vector = self._end_axis_point - self._origin_point # This is the vector that represents one unit of data space in terms of screen space. self._axis_pixel_vector = self._axis_vector/sqrt(dot(self._axis_vector,self._axis_vector)) return #------------------------------------------------------------------------ # Event handlers #------------------------------------------------------------------------ def _bounds_changed(self, old, new): super(PlotAxis, self)._bounds_changed(old, new) self._layout_needed = True self._invalidate() def _bounds_items_changed(self, event): super(PlotAxis, self)._bounds_items_changed(event) self._layout_needed = True self._invalidate() def _mapper_changed(self, old, new): if old is not None: old.on_trait_change(self.mapper_updated, "updated", remove=True) if new is not None: new.on_trait_change(self.mapper_updated, "updated") self._invalidate() def mapper_updated(self): """ Event handler that is bound to this axis's mapper's **updated** event """ self._invalidate() def _position_changed(self, old, new): super(PlotAxis, self)._position_changed(old, new) self._cache_valid = False def _position_items_changed(self, event): super(PlotAxis, self)._position_items_changed(event) self._cache_valid = False def _position_changed_for_component(self): self._cache_valid = False def _position_items_changed_for_component(self): self._cache_valid = False def _bounds_changed_for_component(self): self._cache_valid = False self._layout_needed = True def _bounds_items_changed_for_component(self): self._cache_valid = False self._layout_needed = True def _origin_changed_for_component(self): self._invalidate() def _updated_fired(self): """If the axis bounds changed, redraw.""" self._cache_valid = False return def _invalidate(self): self._cache_valid = False self.invalidate_draw() if self.component: self.component.invalidate_draw() return def _component_changed(self): if self.mapper is not None: # If there is a mapper set, just leave it be. return # Try to pick the most appropriate mapper for our orientation # and what information we can glean from our component. attrmap = { "left": ("ymapper", "y_mapper", "value_mapper"), "bottom": ("xmapper", "x_mapper", "index_mapper"), } attrmap["right"] = attrmap["left"] attrmap["top"] = attrmap["bottom"] component = self.component attr1, attr2, attr3 = attrmap[self.orientation] for attr in attrmap[self.orientation]: if hasattr(component, attr): self.mapper = getattr(component, attr) break # Keep our origin in sync with the component self.origin = getattr(component, 'origin', 'bottom left') return #------------------------------------------------------------------------ # The following event handlers just invalidate our previously computed # Label instances and backbuffer if any of our visual attributes change. # TODO: refactor this stuff and the caching of contained objects (e.g. Label) #------------------------------------------------------------------------ def _title_changed(self): self.invalidate_draw() if self.component: self.component.invalidate_draw() return def _anytrait_changed(self, name, old, new): """ For every trait that defines a visual attribute we just call _invalidate() when a change is made. """ invalidate_traits = [ 'title_font', 'title_spacing', 'title_color', 'tick_weight', 'tick_color', 'tick_label_font', 'tick_label_color', 'tick_label_rotate_angle', 'tick_label_alignment', 'tick_label_margin', 'tick_label_offset', 'tick_label_position', 'tick_label_formatter', 'tick_in', 'tick_out', 'tick_visible', 'tick_interval', 'tick_generator', 'orientation', 'origin', 'axis_line_visible', 'axis_line_color', 'axis_line_weight', 'axis_line_style', 'small_haxis_style', 'ensure_labels_bounded', 'ensure_ticks_bounded', ] if name in invalidate_traits: self._invalidate() #------------------------------------------------------------------------ # Persistence-related methods #------------------------------------------------------------------------ def __getstate__(self): dont_pickle = [ '_tick_list', '_tick_positions', '_tick_label_list', '_tick_label_positions', '_tick_label_bounding_boxes', '_major_axis_size', '_minor_axis_size', '_major_axis', '_title_orientation', '_title_angle', '_origin_point', '_inside_vector', '_axis_vector', '_axis_pixel_vector', '_end_axis_point', '_ticklabel_cache', '_cache_valid' ] state = super(PlotAxis,self).__getstate__() for key in dont_pickle: if state.has_key(key): del state[key] return state def __setstate__(self, state): super(PlotAxis,self).__setstate__(state) self._mapper_changed(None, self.mapper) self._reset_cache() self._cache_valid = False return # EOF ######################################################################## chaco-4.5.0/chaco/axis_view.py0000644000076600000240000000436212426452312017010 0ustar jrocherstaff00000000000000""" Defines the Traits UI view for a PlotAxis """ from traits.api import TraitError from traitsui.api import View, HGroup, Group, VGroup, Item, TextEditor def float_or_auto(val): """ Validator function that returns *val* if *val* is either a number or the word 'auto'. This is used as a validator for the text editor in the Traits UI for the **tick_interval** trait. """ try: return float(val) except: if isinstance(val, basestring) and val == "auto": return val raise TraitError, "Tick interval must be a number or 'auto'." # Traits UI for a PlotAxis. AxisView = View(VGroup( Group( Item("object.mapper.range.low", label="Low Range"), Item("object.mapper.range.high", label="High Range"), ), Group( Item("title", label="Title", editor=TextEditor()), Item("title_font", label="Font", style="simple"), Item("title_color", label="Color", style="custom"), Item("tick_interval", label="Interval", editor=TextEditor(evaluate=float_or_auto)), label="Main"), Group( Item("tick_color", label="Color", style="custom"), #editor=EnableRGBAColorEditor()), Item("tick_weight", label="Thickness"), #Item("tick_label_font", label="Font"), Item("tick_label_color", label="Label color", style="custom"), #editor=EnableRGBAColorEditor()), HGroup( Item("tick_in", label="Tick in"), Item("tick_out", label="Tick out"), ), Item("tick_visible", label="Visible"), label="Ticks"), Group( Item("axis_line_color", label="Color", style="custom"), #editor=EnableRGBAColorEditor()), Item("axis_line_weight", label="Thickness"), Item("axis_line_visible", label="Visible"), label="Line"), ), buttons = ["OK", "Cancel"] ) chaco-4.5.0/chaco/barplot.py0000644000076600000240000004277312426452312016465 0ustar jrocherstaff00000000000000""" Defines the BarPlot class. """ from __future__ import with_statement import logging from numpy import array, compress, column_stack, invert, isnan, transpose, zeros from traits.api import Any, Bool, Enum, Float, Instance, Property, \ Range, Tuple, cached_property, on_trait_change from enable.api import black_color_trait from kiva.constants import FILL_STROKE # Local relative imports from chaco.abstract_plot_renderer import AbstractPlotRenderer from abstract_mapper import AbstractMapper from array_data_source import ArrayDataSource from base import reverse_map_1d logger = logging.getLogger(__name__) # TODO: make child of BaseXYPlot class BarPlot(AbstractPlotRenderer): """ A renderer for bar charts. """ # The data source to use for the index coordinate. index = Instance(ArrayDataSource) # The data source to use as value points. value = Instance(ArrayDataSource) # The data source to use as "starting" values for the bars. # For instance, if the values are [10, 20] and starting_value # is [3, 7], BarPlot will plot two bars, one between 3 and 10, and # one between 7 and 20 starting_value = Instance(ArrayDataSource) # Labels for the indices. index_mapper = Instance(AbstractMapper) # Labels for the values. value_mapper = Instance(AbstractMapper) # The orientation of the index axis. orientation = Enum("h", "v") # The direction of the index axis with respect to the graphics context's # direction. index_direction = Enum("normal", "flipped") # The direction of the value axis with respect to the graphics context's # direction. value_direction = Enum("normal", "flipped") # Type of width used for bars: # # 'data' # The width is in the units along the x-dimension of the data space. # 'screen' # The width uses a fixed width of pixels. bar_width_type = Enum("data", "screen") # Width of the bars, in data or screen space (determined by # **bar_width_type**). bar_width = Float(10) # Round on rectangle dimensions? This is not strictly an "antialias", but # it has the same effect through exact pixel drawing. antialias = Bool(True) # Width of the border of the bars. line_width = Float(1.0) # Color of the border of the bars. line_color = black_color_trait # Color to fill the bars. fill_color = black_color_trait # The RGBA tuple for rendering lines. It is always a tuple of length 4. # It has the same RGB values as line_color_, and its alpha value is the # alpha value of self.line_color multiplied by self.alpha. effective_line_color = Property(Tuple, depends_on=['line_color', 'alpha']) # The RGBA tuple for rendering the fill. It is always a tuple of length 4. # It has the same RGB values as fill_color_, and its alpha value is the # alpha value of self.fill_color multiplied by self.alpha. effective_fill_color = Property(Tuple, depends_on=['fill_color', 'alpha']) # Overall alpha value of the image. Ranges from 0.0 for transparent to 1.0 alpha = Range(0.0, 1.0, 1.0) #use_draw_order = False # Convenience properties that correspond to either index_mapper or # value_mapper, depending on the orientation of the plot. # Corresponds to either **index_mapper** or **value_mapper**, depending on # the orientation of the plot. x_mapper = Property # Corresponds to either **value_mapper** or **index_mapper**, depending on # the orientation of the plot. y_mapper = Property # Corresponds to either **index_direction** or **value_direction**, # depending on the orientation of the plot. x_direction = Property # Corresponds to either **value_direction** or **index_direction**, # depending on the orientation of the plot y_direction = Property # Convenience property for accessing the index data range. index_range = Property # Convenience property for accessing the value data range. value_range = Property #------------------------------------------------------------------------ # Private traits #------------------------------------------------------------------------ # Indicates whether or not the data cache is valid _cache_valid = Bool(False) # Cached data values from the datasources. If **bar_width_type** is "data", # then this is an Nx4 array of (bar_left, bar_right, start, end) for a # bar plot in normal orientation. If **bar_width_type** is "screen", then # this is an Nx3 array of (bar_center, start, end). _cached_data_pts = Any #------------------------------------------------------------------------ # AbstractPlotRenderer interface #------------------------------------------------------------------------ def __init__(self, *args, **kw): # These Traits depend on others, so we'll defer setting them until # after the HasTraits initialization has been completed. later_list = ['index_direction', 'value_direction'] postponed = {} for name in later_list: if name in kw: postponed[name] = kw.pop(name) super(BarPlot, self).__init__(*args, **kw) # Set any keyword Traits that were postponed. self.set(**postponed) def map_screen(self, data_array): """ Maps an array of data points into screen space and returns it as an array. Implements the AbstractPlotRenderer interface. """ # data_array is Nx2 array if len(data_array) == 0: return [] x_ary, y_ary = transpose(data_array) sx = self.index_mapper.map_screen(x_ary) sy = self.value_mapper.map_screen(y_ary) if self.orientation == "h": return transpose(array((sx,sy))) else: return transpose(array((sy,sx))) def map_data(self, screen_pt): """ Maps a screen space point into the "index" space of the plot. Implements the AbstractPlotRenderer interface. """ if self.orientation == "h": screen_coord = screen_pt[0] else: screen_coord = screen_pt[1] return self.index_mapper.map_data(screen_coord) def map_index(self, screen_pt, threshold=2.0, outside_returns_none=True, index_only=False): """ Maps a screen space point to an index into the plot's index array(s). Implements the AbstractPlotRenderer interface. """ data_pt = self.map_data(screen_pt) if ((data_pt < self.index_mapper.range.low) or \ (data_pt > self.index_mapper.range.high)) and outside_returns_none: return None index_data = self.index.get_data() value_data = self.value.get_data() if len(value_data) == 0 or len(index_data) == 0: return None try: ndx = reverse_map_1d(index_data, data_pt, self.index.sort_order) except IndexError: return None x = index_data[ndx] y = value_data[ndx] result = self.map_screen(array([[x,y]])) if result is None: return None sx, sy = result[0] if index_only and ((screen_pt[0]-sx) < threshold): return ndx elif ((screen_pt[0]-sx)**2 + (screen_pt[1]-sy)**2 < threshold*threshold): return ndx else: return None #------------------------------------------------------------------------ # PlotComponent interface #------------------------------------------------------------------------ def _gather_points(self): """ Collects data points that are within the range of the plot, and caches them in **_cached_data_pts**. """ index, index_mask = self.index.get_data_mask() value, value_mask = self.value.get_data_mask() if not self.index or not self.value: return if len(index) == 0 or len(value) == 0 or len(index) != len(value): logger.warn("Chaco: using empty dataset; index_len=%d, value_len=%d." \ % (len(index), len(value))) self._cached_data_pts = array([]) self._cache_valid = True return # TODO: Until we code up a better handling of value-based culling that # takes into account starting_value and dataspace bar widths, just use # the index culling for now. # value_range_mask = self.value_mapper.range.mask_data(value) # nan_mask = invert(isnan(index_mask)) & invert(isnan(value_mask)) # point_mask = index_mask & value_mask & nan_mask & \ # index_range_mask & value_range_mask index_range_mask = self.index_mapper.range.mask_data(index) nan_mask = invert(isnan(index_mask)) point_mask = index_mask & nan_mask & index_range_mask if self.starting_value is None: starting_values = zeros(len(index)) else: starting_values = self.starting_value.get_data() if self.bar_width_type == "data": half_width = self.bar_width / 2.0 points = column_stack((index-half_width, index+half_width, starting_values, value)) else: points = column_stack((index, starting_values, value)) self._cached_data_pts = compress(point_mask, points, axis=0) self._cache_valid = True return def _draw_plot(self, gc, view_bounds=None, mode="normal"): """ Draws the 'plot' layer. """ if not self._cache_valid: self._gather_points() data = self._cached_data_pts if data.size == 0: # Nothing to draw. return with gc: gc.clip_to_rect(self.x, self.y, self.width, self.height) gc.set_antialias(self.antialias) gc.set_stroke_color(self.effective_line_color) gc.set_fill_color(self.effective_fill_color) gc.set_line_width(self.line_width) if self.bar_width_type == "data": # map the bar start and stop locations into screen space lower_left_pts = self.map_screen(data[:,(0,2)]) upper_right_pts = self.map_screen(data[:,(1,3)]) else: half_width = self.bar_width / 2.0 # map the bar centers into screen space and then compute the bar # start and end positions lower_left_pts = self.map_screen(data[:,(0,1)]) upper_right_pts = self.map_screen(data[:,(0,2)]) lower_left_pts[:,0] -= half_width upper_right_pts[:,0] += half_width bounds = upper_right_pts - lower_left_pts gc.rects(column_stack((lower_left_pts, bounds))) gc.draw_path() def _draw_default_axes(self, gc): if not self.origin_axis_visible: return with gc: gc.set_stroke_color(self.origin_axis_color_) gc.set_line_width(self.origin_axis_width) gc.set_line_dash(None) for range in (self.index_mapper.range, self.value_mapper.range): if (range.low < 0) and (range.high > 0): if range == self.index_mapper.range: dual = self.value_mapper.range data_pts = array([[0.0,dual.low], [0.0, dual.high]]) else: dual = self.index_mapper.range data_pts = array([[dual.low,0.0], [dual.high,0.0]]) start,end = self.map_screen(data_pts) gc.move_to(int(start[0])+0.5, int(start[1])+0.5) gc.line_to(int(end[0])+0.5, int(end[1])+0.5) gc.stroke_path() return def _render_icon(self, gc, x, y, width, height): with gc: gc.set_fill_color(self.effective_fill_color) gc.set_stroke_color(self.effective_line_color) gc.rect(x+width/4, y+height/4, width/2, height/2) gc.draw_path(FILL_STROKE) def _post_load(self): super(BarPlot, self)._post_load() return #------------------------------------------------------------------------ # Properties #------------------------------------------------------------------------ def _get_index_range(self): return self.index_mapper.range def _set_index_range(self, val): self.index_mapper.range = val def _get_value_range(self): return self.value_mapper.range def _set_value_range(self, val): self.value_mapper.range = val def _get_x_mapper(self): if self.orientation == "h": return self.index_mapper else: return self.value_mapper def _get_y_mapper(self): if self.orientation == "h": return self.value_mapper else: return self.index_mapper def _get_x_direction(self): if self.orientation == "h": return self.index_direction else: return self.value_direction def _get_y_direction(self): if self.orientation == "h": return self.value_direction else: return self.index_direction #------------------------------------------------------------------------ # Event handlers - these are mostly copied from BaseXYPlot #------------------------------------------------------------------------ def _update_mappers(self): """ Updates the index and value mappers. Called by trait change handlers for various traits. """ x_mapper = self.index_mapper y_mapper = self.value_mapper x_dir = self.index_direction y_dir = self.value_direction if self.orientation == "v": x_mapper, y_mapper = y_mapper, x_mapper x_dir, y_dir = y_dir, x_dir x = self.x x2 = self.x2 y = self.y y2 = self.y2 if x_mapper is not None: if x_dir =="normal": x_mapper.low_pos = x x_mapper.high_pos = x2 else: x_mapper.low_pos = x2 x_mapper.high_pos = x if y_mapper is not None: if y_dir == "normal": y_mapper.low_pos = y y_mapper.high_pos = y2 else: y_mapper.low_pos = y2 y_mapper.high_pos = y self.invalidate_draw() self._cache_valid = False @on_trait_change('line_color, line_width, fill_color, alpha') def _attributes_changed(self): self.invalidate_draw() self.request_redraw() def _bounds_changed(self, old, new): super(BarPlot, self)._bounds_changed(old, new) self._update_mappers() def _bounds_items_changed(self, event): super(BarPlot, self)._bounds_items_changed(event) self._update_mappers() def _orientation_changed(self): self._update_mappers() def _index_changed(self, old, new): if old is not None: old.on_trait_change(self._either_data_changed, "data_changed", remove=True) if new is not None: new.on_trait_change(self._either_data_changed, "data_changed") self._either_data_changed() def _index_direction_changed(self): m = self.index_mapper m.low_pos, m.high_pos = m.high_pos, m.low_pos self.invalidate_draw() def _value_direction_changed(self): m = self.value_mapper m.low_pos, m.high_pos = m.high_pos, m.low_pos self.invalidate_draw() def _either_data_changed(self): self.invalidate_draw() self._cache_valid = False self.request_redraw() def _value_changed(self, old, new): if old is not None: old.on_trait_change(self._either_data_changed, "data_changed", remove=True) if new is not None: new.on_trait_change(self._either_data_changed, "data_changed") self._either_data_changed() def _index_mapper_changed(self, old, new): return self._either_mapper_changed(old, new) def _value_mapper_changed(self, old, new): return self._either_mapper_changed(old, new) def _either_mapper_changed(self, old, new): if old is not None: old.on_trait_change(self._mapper_updated_handler, "updated", remove=True) if new is not None: new.on_trait_change(self._mapper_updated_handler, "updated") self.invalidate_draw() def _mapper_updated_handler(self): self._cache_valid = False self.invalidate_draw() self.request_redraw() def _bar_width_changed(self): self._cache_valid = False self.invalidate_draw() self.request_redraw() def _bar_width_type_changed(self): self._cache_valid = False self.invalidate_draw() self.request_redraw() #------------------------------------------------------------------------ # Property getters #------------------------------------------------------------------------ @cached_property def _get_effective_line_color(self): if len(self.line_color_) == 4: line_alpha = self.line_color_[-1] else: line_alpha = 1.0 c = self.line_color_[:3] + (line_alpha * self.alpha,) return c @cached_property def _get_effective_fill_color(self): if len(self.fill_color_) == 4: fill_alpha = self.fill_color_[-1] else: fill_alpha = 1.0 c = self.fill_color_[:3] + (fill_alpha * self.alpha,) return c ### EOF #################################################################### chaco-4.5.0/chaco/base.py0000644000076600000240000001621112426452312015720 0ustar jrocherstaff00000000000000""" Defines basic traits and functions for the data model. """ # Standard library imports from math import radians, sqrt # Major library imports from numpy import (array, argsort, concatenate, cos, dot, empty, nonzero, pi, sin, take, ndarray) # Enthought library imports from traits.api import CArray, Enum, Trait # Dimensions # A single array of numbers. NumericalSequenceTrait = Trait(None, None, CArray(value=empty(0))) # A sequence of pairs of numbers, i.e., an Nx2 array. PointTrait = Trait(None, None, CArray(value=empty(0))) # An NxM array of numbers. ImageTrait = Trait(None, None, CArray(value=empty(0))) # An 3D array of numbers of shape (Nx, Ny, Nz) CubeTrait = Trait(None, None, CArray(value=empty(0))) # This enumeration lists the fundamental mathematical coordinate types that # Chaco supports. DimensionTrait = Enum("scalar", "point", "image", "cube") # Linear sort order. SortOrderTrait = Enum("ascending", "descending", "none") #---------------------------------------------------------------------------- # Utility functions #---------------------------------------------------------------------------- def poly_point(center, r, degrees): x = r * cos(degrees) + center[0] y = r * sin(degrees) + center[1] return x,y def n_gon(center, r, nsides, rot_degrees=0): """ Generates the points of a regular polygon with specified center, radius, and number of sides. By default the rightmost point of the polygon is (*r*,0) but a rotation about the center may be specified with *rot_degrees*. """ if nsides < 3: raise ValueError, 'Must have at least 3 sides in a polygon' rotation = radians(rot_degrees) theta = (pi * 2) / nsides return [poly_point(center, r, i*theta+rotation) for i in range(nsides)] # Ripped from Chaco 1.0's plot_base.py def bin_search(values, value, ascending): """ Performs a binary search of a sorted array looking for a specified value. Returns the lowest position where the value can be found or where the array value is the last value less (greater) than the desired value. Returns -1 if *value* is beyond the minimum or maximum of *values*. """ if ascending > 0: if (value < values[0]) or (value > values[-1]): return -1 else: if (value < values[-1]) or (value > values[0]): return -1 lo = 0 hi = len( values ) while True: mid = (hi + lo) / 2 test = cmp( values[ mid ], value ) * ascending if test == 0: return mid if test > 0: hi = mid else: lo = mid if lo >= (hi - 1): return lo def reverse_map_1d(data, pt, sort_order, floor_only=False): """Returns the index of *pt* in the array *data*. Raises IndexError if *pt* is outside the range of values in *data*. Parameters ---------- data : 1-D array data to search pt : scalar value value to find, which must be within the value range of *data* sort_order : string "ascending" or "descending" floor_only : bool if true, don't find "nearest" point, instead find last point less (greater) than pt """ if sort_order == "ascending": ndx = bin_search(data, pt, 1) elif sort_order == "descending": ndx = bin_search(data, pt, -1) else: raise NotImplementedError, "reverse_map_1d() requires a sorted array" if ndx == -1: raise IndexError, "value outside array data range" # Now round the index to the closest matching index. Do this # by determining the width (in value space) of each cell and # figuring out which side of the midpoint pt falls into. Since # bin_search rounds down (i.e. each cell index contains the point # and all points up to the next cell index), we only need to look # at ndx+1 and not ndx-1 as well. last = len(data) - 1 if ndx < last: if floor_only: return ndx delta = 0.5 * (data[ndx+1] - data[ndx]) if ((sort_order == "ascending") and (pt > data[ndx] + delta)) or \ ((sort_order == "descending") and (pt < data[ndx] + delta)): return ndx + 1 else: return ndx else: # NB: OK floor_only is typically used with image plots, which # will have one extra "fencepost" so the assumption here is that # if we hit the last point exactly we still really want the index # of the previous point if floor_only: return last-1 # If pt happened to match the value of data[last] exactly, # we just return it here. return last # These are taken from Chaco 1.0's datamapper and subdivision_cells modules. # TODO: Write unit tests for these! def right_shift(ary, newval): "Returns a right-shifted version of *ary* with *newval* inserted on the left." return concatenate([[newval], ary[:-1]]) def left_shift(ary, newval): "Returns a left-shifted version of *ary* with *newval* inserted on the right." return concatenate([ary[1:], [newval]]) def sort_points(points, index=0): """ sort_points(array_of_points, index=<0|1>) -> sorted_array Takes a list of points as an Nx2 array and sorts them according to their x- or y-coordinate. If *index* is zero, the points are sorted on their x-coordinate. """ if len(points.shape) != 2 or (2 not in points.shape): raise RuntimeError, "sort_points(): Array of wrong shape." return take( points, argsort(points[:,index]) ) def find_runs(int_array, order='ascending'): """ find_runs(int_array, order=<'ascending'|'flat'|'descending'>) -> list_of_int_arrays Given an integer array sorted in ascending/descending order or flat order, returns a list of continuous runs of integers inside the list. for example:: find_runs([1,2,3,6,7,8,9,10,11,15]) returns [ [1,2,3], [6,7,8,9,10,11], [15] ] and:: find_runs([0,0,0,1,1,1,1,0,0,0,0], "flat") return [ [0,0,0], [1,1,1,1], [0,0,0,0] ] """ ranges = arg_find_runs(int_array, order) if ranges: return [int_array[i:j] for (i,j) in ranges] else: return [] def arg_find_runs(int_array, order='ascending'): """ Like find_runs(), but returns a list of tuples indicating the start and end indices of runs in the input *int_array*. """ if len(int_array) == 0: return [] assert len(int_array.shape)==1, "find_runs() requires a 1D integer array." if order == 'ascending': increment = 1 elif order == 'descending': increment = -1 else: increment = 0 rshifted = right_shift(int_array, int_array[0]-increment).view(ndarray) start_indices = concatenate([[0], nonzero(int_array - (rshifted+increment))[0]]) end_indices = left_shift(start_indices, len(int_array)) return zip(start_indices, end_indices) def point_line_distance(pt, p1, p2): """ Returns the perpendicular distance between *pt* and the line segment between the points *p1* and *p2*. """ v1 = array((pt[0] - p1[0], pt[1] - p1[1])) v2 = array((p2[0] - p1[0], p2[1] - p1[1])) diff = v1 - dot(v1, v2) / dot(v2, v2) * v2 return sqrt(dot(diff,diff)) #EOF chaco-4.5.0/chaco/base_1d_mapper.py0000644000076600000240000001041012426462410017643 0ustar jrocherstaff00000000000000""" Defines the Base1DMapper class. """ # Enthought library imports from traits.api import Bool, Instance, Float, Property # Local relative imports from abstract_mapper import AbstractMapper from data_range_1d import DataRange1D class Base1DMapper(AbstractMapper): """ Defines an abstract mapping from a 1-D region in input space to a 1-D region in output space. """ # The data-space bounds of the mapper. range = Instance(DataRange1D) # The screen space position of the lower bound of the data space. low_pos = Float(0.0) # The screen space position of the upper bound of the data space. high_pos = Float(1.0) # Convenience property to get low and high positions in one structure. # Must be a tuple (low_pos, high_pos). screen_bounds = Property # Should the mapper stretch the dataspace when its screen space bounds are # modified (default), or should it preserve the screen-to-data ratio and # resize the data bounds? If the latter, it will only try to preserve # the ratio if both screen and data space extents are non-zero. stretch_data = Bool(True) # The sign of the mapping: 1 if deltas match sign, -1 if opposite sign sign = Property # If the subclass uses a cache, _cache_valid is maintained to # monitor its status _cache_valid = Bool(False) # Indicates whether or not the bounds have been set at all, or if they # are at their initial default values. _low_bound_initialized = Bool(False) _high_bound_initialized = Bool(False) #------------------------------------------------------------------------ # Event handlers #------------------------------------------------------------------------ def _low_pos_changed(self, old, new): self._cache_valid = False if not self.stretch_data: self._adjust_range((old, self.high_pos), (new, self.high_pos)) self._low_bound_initialized = True self.updated = True def _high_pos_changed(self, old, new): self._cache_valid = False if not self.stretch_data: self._adjust_range((self.low_pos, old), (self.low_pos, new)) self._high_bound_initialized = True self.updated = True def _range_changed(self, old, new): if old is not None: old.on_trait_change(self._range_change_handler, "updated", remove = True) if new is not None: new.on_trait_change(self._range_change_handler, "updated") self._cache_valid = False self.updated = new return def _range_change_handler(self, obj, name, new): "Handles the range changing; dynamically attached to our ranges" self._cache_valid = False self.updated = obj return def _get_screen_bounds(self): return (self.low_pos, self.high_pos) def _get_sign(self): delta_screen = (self.high_pos - self.low_pos) delta_data = (self.range.high-self.range.low) if delta_screen == 0 or delta_data == 0: return 0 elif delta_screen/float(delta_data) < 0: return -1 else: return 1 def _set_screen_bounds(self, new_bounds): if new_bounds[0] == self.low_pos and new_bounds[1] == self.high_pos: return if not self.stretch_data: self._adjust_range((self.low_pos, self.high_pos), new_bounds) self.set(low_pos = new_bounds[0], trait_change_notify=False) self.set(high_pos = new_bounds[1], trait_change_notify=False) self._cache_valid = False self._low_bound_initialized = True self._high_bound_initialized = True self.updated = True return def _adjust_range(self, old_bounds, new_bounds): initialized = self._low_bound_initialized and \ self._high_bound_initialized if self.range is not None and initialized: rangelow = self.range.low rangehigh = self.range.high d_data = rangehigh - rangelow old_d_screen = old_bounds[1] - old_bounds[0] if d_data != 0 and old_d_screen != 0: new_data_extent = d_data / old_d_screen * (new_bounds[1] - new_bounds[0]) self.range.set_bounds(rangelow, rangelow + new_data_extent) chaco-4.5.0/chaco/base_2d_plot.py0000644000076600000240000002662412426452312017354 0ustar jrocherstaff00000000000000""" Defines the base class for 2-D plots. """ # Standard library imports from numpy import asarray, isnan # Enthought library imports. from traits.api import Enum, Event, Instance, Property, Range, Trait # Local relative imports from abstract_plot_renderer import AbstractPlotRenderer from base import reverse_map_1d from plot_label import PlotLabel from grid_data_source import GridDataSource from grid_mapper import GridMapper from image_data import ImageData class Base2DPlot(AbstractPlotRenderer): """ Base class for 2-D plots. """ #------------------------------------------------------------------------ # Data-related traits #------------------------------------------------------------------------ # The data source to use for the index coordinate. index = Instance(GridDataSource) # The data source to use as value points. value = Instance(ImageData) # Screen mapper for 2-D structured (gridded) index data. index_mapper = Instance(GridMapper) # Convenience property for accessing the data range of the mapper. index_range = Property # Convenience property for accessing the plots labels. labels = Property # The direction that the first array returned by self.index.get_data() # maps to. # # * 'h': index maps to x-direction # * 'v': index maps to y-direction orientation = Enum("h", "v") # Overrides PlotComponent; 2-D plots draw on the 'image' layer, # underneath all decorations and annotations, and above only the background # fill color. draw_layer = "image" # Convenience property for accessing the x-direction mappers regardless # of orientation. This provides compatibility with a number of tools. x_mapper = Property # Convenience property for accessing the y-direction mappers regardless # of orientation. This provides compatibility with a number of tools. y_mapper = Property # Overall alpha value of the image. Ranges from 0.0 for transparent to 1.0 # for full intensity. alpha = Trait(1.0, Range(0.0, 1.0)) # Event fired when the index data changes. Subclasses can listen for this # event and take appropriate steps (except for requesting a redraw, which # is done in this class). index_data_changed = Event # Event fired when the index mapper changes. Subclasses can listen for this # event and take appropriate steps (except for requesting a redraw, which # is done in this class). index_mapper_changed = Event # Event fired when the value data changes. Subclasses can listen for this # event and take appropriate steps (except for requesting a redraw, which # is done in this class). value_data_changed = Event #------------------------------------------------------------------------ # Public methods #------------------------------------------------------------------------ def __init__(self, **kwargs): # Handling the setting/initialization of these traits manually because # they should be initialized in a certain order. kwargs_tmp = {"trait_change_notify": False} for trait_name in ("index", "value"): if trait_name in kwargs: kwargs_tmp[trait_name] = kwargs.pop(trait_name) self.set(**kwargs_tmp) super(Base2DPlot, self).__init__(**kwargs) if self.index is not None: self.index.on_trait_change(self._update_index_data, "data_changed") if self.index_mapper: self.index_mapper.on_trait_change(self._update_index_mapper, "updated") if self.value is not None: self.value.on_trait_change(self._update_value_data, "data_changed") # If we are not resizable, we will not get a bounds update upon layout, # so we have to manually update our mappers if self.resizable == "": self._update_mappers() return #------------------------------------------------------------------------ # AbstractPlotRenderer interface #------------------------------------------------------------------------ def map_screen(self, data_pts): """ Maps an array of data points into screen space and returns it as an array. Implements the AbstractPlotRenderer interface. """ # data_pts is Nx2 array if len(data_pts) == 0: return [] return asarray(self.index_mapper.map_screen(data_pts)) def map_data(self, screen_pts): """ Maps a screen space point into the "index" space of the plot. Implements the AbstractPlotRenderer interface. """ return self.index_mapper.map_data(screen_pts) def map_index(self, screen_pt, threshold=2.0, outside_returns_none=True, index_only=False): """ Maps a screen space point to an index into the plot's index arrays. Implements the AbstractPlotRenderer interface. The *index_only* parameter is ignored because the index is intrinsically 2-D. """ if self.orientation == 'h': x_pt,y_pt = self.map_data([screen_pt])[0] else: x_pt,y_pt = self.map_data([(screen_pt[1],screen_pt[0])])[0] if ((x_pt < self.index_mapper.range.low[0]) or (x_pt > self.index_mapper.range.high[0]) or (y_pt < self.index_mapper.range.low[1]) or (y_pt > self.index_mapper.range.high[1])) and outside_returns_none: return None, None x_index_data, y_index_data = self.index.get_data() if x_index_data.get_size() == 0 or y_index_data.get_size() == 0: return None, None # attempt to map to the x index x_data = x_index_data.get_data() y_data = y_index_data.get_data() try: x_ndx = reverse_map_1d(x_data, x_pt, self.index.sort_order[0], floor_only=True) except IndexError, e: if outside_returns_none: return None, None # x index if x_pt < x_data[0]: x_ndx = 0 else: x_ndx = len(x_data) - 1 try: y_ndx = reverse_map_1d(y_data, y_pt, self.index.sort_order[1], floor_only=True) except IndexError, e: if outside_returns_none: return None, None # y index if y_pt < y_data[0]: y_ndx = 0 else: y_ndx = len(y_data) - 1 if threshold == 0: return x_ndx, y_ndx x = x_data[x_ndx] y = y_data[y_ndx] if isnan(x) or isnan(y): return None, None sx, sy = self.map_screen([(x,y)])[0] if ((screen_pt[0]-sx)**2 + (screen_pt[1]-sy)**2 < threshold**2): return x_ndx, y_ndx else: return None, None #------------------------------------------------------------------------ # PlotComponent interface #------------------------------------------------------------------------ def _draw_image(self, gc, view_bounds=None, mode="normal"): """ Handler for drawing the 'image' layer. Used by the PlotComponent interface. """ self._render(gc) return #------------------------------------------------------------------------ # Abstract methods that subclasses must implement #------------------------------------------------------------------------ def _render(self, gc, points): """ Abstract method for drawing the plot. """ raise NotImplementedError #------------------------------------------------------------------------ # Properties #------------------------------------------------------------------------ def _get_index_range(self): return self.index_mapper.range def _set_index_range(self, val): self.index_mapper.range = val def _get_labels(self): labels = [] for obj in self.underlays+self.overlays: if isinstance(obj, PlotLabel): labels.append(obj) return labels def _get_x_mapper(self): if self.orientation == 'h': return self.index_mapper._xmapper else: return self.index_mapper._ymapper def _get_y_mapper(self): if self.orientation == 'h': return self.index_mapper._ymapper else: return self.index_mapper._xmapper #------------------------------------------------------------------------ # Private methods #------------------------------------------------------------------------ def _update_index_mapper(self): """ Updates the index mapper. Called by various trait change handlers. """ x = self.x x2 = self.x2 y = self.y y2 = self.y2 if "left" in self.origin: x_low = x x_high = x2 else: x_low = x2 x_high = x if "bottom" in self.origin: y_low = y y_high = y2 else: y_low = y2 y_high = y if self.index_mapper is not None: if self.orientation == 'h': self.index_mapper.screen_bounds = (x_low, x_high, y_low, y_high) else: self.index_mapper.screen_bounds = (y_low, y_high, x_low, x_high) self.index_mapper_changed = True self.invalidate_draw() def _update_index_data(self): """ Updates the index data. Called by various trait change handlers. """ self.index_data_changed = True self.invalidate_draw() def _update_value_data(self): """ Updates the value data. Called by various trait change handlers. """ self.value_data_changed = True self.invalidate_draw() #------------------------------------------------------------------------ # Event handlers #------------------------------------------------------------------------ def _bounds_changed(self, old, new): super(Base2DPlot, self)._bounds_changed(old, new) self._update_index_mapper() def _bounds_items_changed(self, event): super(Base2DPlot, self)._bounds_items_changed(event) self._update_index_mapper() def _orientation_changed(self): self._update_index_mapper() def _origin_changed(self): self._update_index_mapper() def _index_changed(self, old, new): if old is not None: old.on_trait_change(self._update_index_data, "data_changed", remove=True) if new is not None: new.on_trait_change(self._update_index_data, "data_changed") self._update_index_data() def _value_changed(self, old, new): if old is not None: old.on_trait_change(self._update_value_data, "data_changed", remove=True) if new is not None: new.on_trait_change(self._update_value_data, "data_changed") self._update_value_data() def _index_mapper_changed(self, old, new): if old is not None: old.on_trait_change(self._update_index_mapper, "updated", remove=True) if new is not None: new.on_trait_change(self._update_index_mapper, "updated") self._update_index_mapper() chaco-4.5.0/chaco/base_candle_plot.py0000644000076600000240000001462512426452312020273 0ustar jrocherstaff00000000000000 from __future__ import with_statement # Major library imports from numpy import array, column_stack # Enthought library imports from enable.api import ColorTrait from traits.api import Bool, Float, Int, List, Property, Trait # Chaco imports from base_xy_plot import BaseXYPlot # TODO: allow to set the width of the bar def Alias(name): return Property(lambda obj: getattr(obj, name), lambda obj, val: setattr(obj, name, val)) class BaseCandlePlot(BaseXYPlot): """ Represents the base class for candle- and bar-type plots that are multi-valued at each index point, and optionally have an extent in the index dimension. Implements the rendering logic and centralizes a lot of the visual attributes for these sorts of plots. The gather and culling and clipping of data is up to individual subclasses. """ #------------------------------------------------------------------------ # Appearance traits #------------------------------------------------------------------------ # The fill color of the marker. color = ColorTrait("black") # The fill color of the bar bar_color = Alias("color") # The color of the rectangular box forming the bar. bar_line_color = Alias("outline_color") # The color of the stems reaching from the bar ends to the min and max # values. Also the color of the endcap line segments at min and max. If # None, this defaults to **bar_line_color**. stem_color = Trait(None, None, ColorTrait("black")) # The color of the line drawn across the bar at the center values. # If None, this defaults to **bar_line_color**. center_color = Trait(None, None, ColorTrait("black")) # The color of the outline to draw around the bar. outline_color = ColorTrait("black") # The thickness, in pixels, of the outline to draw around the bar. If # this is 0, no outline is drawn. line_width = Float(1.0) # The thickness, in pixels, of the stem lines. If None, this defaults # to **line_width**. stem_width = Trait(None, None, Int(1)) # The thickeness, in pixels, of the line drawn across the bar at the # center values. If None, this defaults to **line_width**. center_width = Trait(None, None, Int(1)) # Whether or not to draw bars at the min and max extents of the error bar end_cap = Bool(True) #------------------------------------------------------------------------ # Private traits #------------------------------------------------------------------------ # Override the base class definition of this because we store a list of # arrays and not a single array. _cached_data_pts = List() #------------------------------------------------------------------------ # BaseXYPlot interface #------------------------------------------------------------------------ def get_screen_points(self): # Override the BaseXYPlot implementation so that this is just # a pass-through, in case anyone calls it. pass #------------------------------------------------------------------------ # Protected methods (subclasses should be able to use these directly # or wrap them) #------------------------------------------------------------------------ def _render(self, gc, right, left, min, bar_min, center, bar_max, max): stack = column_stack with gc: widths = right - left bar_vert_center = left + widths / 2.0 # Draw the stem lines for min to max. Draw these first so we can # draw the boxes on top. # A little tricky: we need to account for cases when either min or max # are None. To do this, just draw to bar_min or from bar_max instead # of drawing a single line from min to max. if min is not None or max is not None: if self.stem_color is None: stem_color = self.outline_color_ else: stem_color = self.stem_color_ gc.set_stroke_color(stem_color) if self.stem_width is None: stem_width = self.line_width else: stem_width = self.stem_width gc.set_line_width(stem_width) if min is None: gc.line_set(stack((bar_vert_center, bar_max)), stack((bar_vert_center, max))) if self.end_cap: gc.line_set(stack((left, max)), stack((right, max))) elif max is None: gc.line_set(stack((bar_vert_center, min)), stack((bar_vert_center, bar_min))) if self.end_cap: gc.line_set(stack((left, min)), stack((right, min))) else: gc.line_set(stack((bar_vert_center, min)), stack((bar_vert_center, max))) if self.end_cap: gc.line_set(stack((left, max)), stack((right, max))) gc.line_set(stack((left, min)), stack((right, min))) gc.stroke_path() # Draw the candlestick boxes boxes = stack((left, bar_min, widths, bar_max - bar_min)) gc.set_antialias(False) gc.set_stroke_color(self.outline_color_) gc.set_line_width(self.line_width) gc.rects(boxes) if self.color in ("none", "transparent", "clear"): gc.stroke_path() else: gc.set_fill_color(self.color_) gc.draw_path() # Draw the center line if center is not None: if self.center_color is None: gc.set_stroke_color(self.outline_color_) else: gc.set_stroke_color(self.center_color_) if self.center_width is None: gc.set_line_width(self.line_width) else: gc.set_line_width(self.center_width) gc.line_set(stack((left, center)), stack((right, center))) gc.stroke_path() def _render_icon(self, gc, x, y, width, height): min = array([y + 1]) max = array([y + height - 1]) bar_min = array([y + height / 3]) bar_max = array([y + height - (height / 3)]) center = array([y + (height / 2)]) self._render(gc, array([x+width/4]), array([x+3*width/4]), min, bar_min, center, bar_max, max) chaco-4.5.0/chaco/base_contour_plot.py0000644000076600000240000001722412426452312020534 0ustar jrocherstaff00000000000000 from numpy import array, isscalar, issubsctype, linspace, number # Enthought library imports from enable.api import ColorTrait from traits.api import Bool, Instance, Int, List, Property, \ Range, Str, Trait, Tuple # Local relative imports from base_2d_plot import Base2DPlot from color_mapper import ColorMapper class BaseContourPlot(Base2DPlot): """ The base class for contour plots. Mostly manages configuration and change events with colormap and contour parameters. """ #------------------------------------------------------------------------ # Data-related traits #------------------------------------------------------------------------ # Defines the levels to contour. # ``levels`` can be either: a list of floating point numbers that define # the value of the function at the contours; a positive integer, in which # case the range of the value is divided in the given number of equally # spaced levels; or "auto" (default), which divides the range in 10 levels levels = Trait("auto", Int, List) # The color(s) of the lines. # ``colors`` can be given as a color name, in which case all contours have # the same color, as a list of colors, or as a colormap. If the list of # colors is shorter than the number of levels, the values are repeated # from the beginning of the list. Default is black. # Colors are associated with levels of increasing value. colors = Trait(None, Str, Instance(ColorMapper), List, Tuple) # If present, the color mapper for the colorbar to look at. color_mapper = Property(Instance(ColorMapper)) # A global alpha value to apply to all the contours alpha = Trait(1.0, Range(0.0, 1.0)) #------------------------------------------------------------------------ # Private traits #------------------------------------------------------------------------ # Is the cached level data valid? _level_cache_valid = Bool(False) # Is the cached color data valid? _colors_cache_valid = Bool(False) # List of levels and their associated line properties. _levels = List # List of colors _colors = List # Mapped trait used to convert user-suppied color values to AGG-acceptable # ones. (Mapped traits in lists are not supported, must be converted one at # a time.) _color_map_trait = ColorTrait def __init__(self, *args, **kwargs): super(BaseContourPlot, self).__init__(*args, **kwargs) if self.color_mapper: self.color_mapper.on_trait_change(self._update_color_mapper, "updated") return def _update_levels(self): """ Updates the levels cache. """ low, high = self.value.get_bounds() if self.levels == "auto": self._levels = list(linspace(low, high, 10)) elif isinstance(self.levels, int): self._levels = list(linspace(low, high, self.levels)) else: self._levels = self.levels self._levels.sort() self._level_cache_valid = True self._colors_cache_valid = False def _update_colors(self, numcolors=None): """ Update the colors cache using our color mapper and based on our number of levels. The **mode** parameter accounts for fenceposting: - If **mode** is "poly", then the number of colors to generate is 1 less than the number of levels - If **mode** is "line", then the number of colors to generate is equal to the number of levels """ if numcolors is None: numcolors = len(self._levels) colors = self.colors # If we are given no colors, set a default for all levels if colors is None: self._color_map_trait = "black" self._colors = [self._color_map_trait_] * numcolors # If we are given a single color, apply it to all levels elif isinstance(colors, basestring): self._color_map_trait = colors self._colors = [self._color_map_trait_] * numcolors # If we are given a colormap, use it to map all the levels to colors elif isinstance(colors, ColorMapper): self._colors = [] mapped_colors = self.color_mapper.map_screen(array(self._levels)) for i in range(numcolors): self._color_map_trait = tuple(mapped_colors[i]) self._colors.append(self._color_map_trait_) # A list or tuple # This could be a length 3 or 4 sequence of scalars, which indicates # a color; otherwise, this is interpreted as a list of items to # be converted via self._color_map_trait. else: if len(colors) in (3,4) and \ (isscalar(colors[0]) and issubsctype(type(colors[0]), number)): self._color_map_trait = colors self._colors = [self._color_map_trait_] * numcolors else: # if the list of colors is shorter than the list of levels, simply # repeat colors from the beginning of the list as needed self._colors = [] for i in range(len(self._levels)): self._color_map_trait = colors[i%len(colors)] self._colors.append(self._color_map_trait_) self._colors_cache_valid = True return #------------------------------------------------------------------------ # Event handlers #------------------------------------------------------------------------ def _index_data_changed_fired(self): # If the index data has changed, the reset the levels cache (which # also triggers all the other caches to reset). self._level_cache_valid = False self.invalidate_draw() def _value_data_changed_fired(self): # If the index data has changed, the reset the levels cache (which # also triggers all the other caches to reset). self._level_cache_valid = False self.invalidate_draw() def _index_mapper_changed_fired(self): # If the index mapper has changed, then we need to redraw self.invalidate_draw() def _update_color_mapper(self): # If the color mapper has changed, then we need to recompute the # levels and cached data associated with that. self._level_cache_valid = False self.invalidate_draw() def _levels_changed(self): self._update_levels() self.invalidate_draw() self.request_redraw() def _colors_changed(self): if self._level_cache_valid: self._update_colors() self.invalidate_draw() #------------------------------------------------------------------------ # Trait properties #------------------------------------------------------------------------ def _get_color_mapper(self): if isinstance(self.colors, ColorMapper): return self.colors else: return None def _set_color_mapper(self, color_mapper): # Remove the dynamic event handler from the old color mapper if self.colors is not None and isinstance(self.colors, ColorMapper): self.colors.on_trait_change(self._update_color_mapper, "updated", remove=True) # Check to see if we should copy over the range as well if color_mapper is not None: if color_mapper.range is None and self.colors.range is not None: color_mapper.range = self.colors.range # Attach the dynamic event handler to the new color mapper if color_mapper is not None: color_mapper.on_trait_change(self._update_color_mapper, "updated") self.colors = color_mapper self._update_color_mapper() chaco-4.5.0/chaco/base_data_range.py0000644000076600000240000000213412426452312020064 0ustar jrocherstaff00000000000000""" Defines the BaseDataRange class. """ # Local relative imports from abstract_data_range import AbstractDataRange class BaseDataRange(AbstractDataRange): """ Ranges represent sub-regions of data space. They support "autoscaling" by querying their associated data sources. """ #------------------------------------------------------------------------ # Public methods #------------------------------------------------------------------------ def __init__(self, *datasources, **kwtraits): super(AbstractDataRange, self).__init__(**kwtraits) if len(datasources) > 0: self.sources.extend(datasources) def add(self, *datasources): """ Convenience method to add a data source. """ for datasource in datasources: if datasource not in self.sources: self.sources.append(datasource) def remove(self, *datasources): """ Convenience method to remove a data source. """ for datasource in datasources: if datasource in self.sources: self.sources.remove(datasource) chaco-4.5.0/chaco/base_plot_container.py0000644000076600000240000000534612426452312021027 0ustar jrocherstaff00000000000000""" Defines the BasePlotContainer class. """ import warnings # Enthought library imports from enable.api import Container from traits.api import Bool, Instance, Property, Str, Tuple # Local, relative imports from plot_component import DEFAULT_DRAWING_ORDER, PlotComponent class BasePlotContainer(Container): """ A container for PlotComponents that conforms to being laid out by PlotFrames. Serves as the base class for other PlotContainers. PlotContainers define a layout, i.e., a spatial relationship between their contained components. (BasePlotContainer doesn't define one, but its various subclasses do.) BasePlotContainer is a subclass of Enable Container, so it is possible to insert Enable-level components into it. However, because Enable components don't have the correct interfaces to participate in layout, the visual results will probably be incorrect. """ # Redefine the container layers to name the main layer as "plot" instead # of the Enable default of "mainlayer" container_under_layers = Tuple("background", "image", "underlay", "plot") #------------------------------------------------------------------------ # Duplicate trait declarations from PlotComponent. We don't subclass # PlotComponent to avoid MRO complications with trait handlers and property # getters/setters. #------------------------------------------------------------------------ draw_order = Instance(list, args=(DEFAULT_DRAWING_ORDER,)) draw_layer = Str("plot") #------------------------------------------------------------------------ # Deprecated traits #------------------------------------------------------------------------ # Deprecated flag to indicate that a component needed to do old-style # drawing. Unused by any recent Chaco component. use_draw_order = Bool(True) # Deprecated property for accessing the components in the container. plot_components = Property def _get_plot_components(self): warnings.warn("Use of plot_components attribute deprecated." \ "Use components attribute instead.", DeprecationWarning) return self._components def _set_plot_components(self, new): warnings.warn("Use of plot_components attribute deprecated." \ "Use components attribute instead.", DeprecationWarning) self._components = new def _use_draw_order_changed(self, old, new): """ Handler to catch the case when someone is trying to use the old-style drawing mechanism, which is now unsupported. """ if new == False: raise RuntimeError("The old-style drawing mechanism is no longer " \ "supported in Chaco.") # EOF chaco-4.5.0/chaco/base_plot_frame.py0000644000076600000240000001433512426452312020135 0ustar jrocherstaff00000000000000""" Defines the BasePlotFrame class (deprecated). """ ################################################################################# # # NOTE: PlotFrames are deprecated. There is no need to use them any more. # This class will be removed some time in the near future. # ################################################################################# from __future__ import with_statement # Enthought library imports from enable.api import Container from traits.api import Enum # Local, relative imports from plot_component import PlotComponent, DEFAULT_DRAWING_ORDER class BasePlotFrame(Container, PlotComponent): """ Base class for plot frames. Primarily defines the basic functionality of managing slots (sub-containers) within the plot frame. NOTE: PlotFrames are deprecated. There is no need to use them any more. This class will be removed some time in the near future. """ # A named list of places/positions/"slots" on the frame where PlotComponents # can place themselves. Subclasses must redefine this trait with the # appropriate values. Note that by default, __getattr__ treats these # slot names as attributes on the class so they can be directly accessed. # This is a class attribute. slot_names = () # Dimensions in which this frame can resize to fit its components. # This is similar to the **resizable** trait on PlotComponent. Chaco # plot frames use this attribute in preference to the Enable # **auto_size** attribute (which is overridden to be False by default). fit_components = Enum("", "h", "v", "hv") # Overrides the Enable auto_size trait (which will be deprecated in the future) auto_size = False draw_order = DEFAULT_DRAWING_ORDER def __init__(self, **kw): self._frame_slots = {} super(BasePlotFrame, self).__init__(**kw) return def add_to_slot(self, slot, component, stack="overlay"): """ Adds a component to the named slot using the given stacking mode. The valid modes are: 'overlay', 'left', 'right', 'top', 'bottom'. """ self.frame_slots[slot].add_plot_component(component, stack) return def set_slot(self, slotname, container): """ Sets the named slot to use the given container. *container* can be None. """ if self._frame_slots.has_key(slotname): old_container = self._frame_slots[slotname] Container.remove(self, old_container) if container is not None: self._frame_slots[slotname] = container Container.add(self, container) return def get_slot(self, slotname): """ Returns the container in the named slot. """ return self._frame_slots.get(slotname, None) #------------------------------------------------------------------------ # PlotComponent interface #------------------------------------------------------------------------ def draw(self, gc, view_bounds=None, mode="normal"): """ Draws the plot frame. Frames are the topmost Chaco component that knows about layout, and they are the start of the layout pipeline. When they are asked to draw, they can assume that their own size has been set properly and this in turn drives the layout of the contained components within the trame. """ self.do_layout() #if gc.window and gc.window.is_sizing: if 0: with gc: gc.translate_ctm(*self.position) #TODO: We are ignoring Container... PlotComponent.draw(self, gc, view_bounds, "interactive") else: super(BasePlotFrame, self).draw(gc, view_bounds, mode) return def do_layout(self, size=None, force=False): """ Tells this frame to do layout at a given size. Overrides PlotComponent. If this frame needs to fit components in at least one dimension, then it checks whether any of them need to do layout; if so, the frame needs to do layout also. """ if not self._layout_needed and not force and self.fit_components != "": for slot in self._frame_slots.values(): if slot._layout_needed: self._layout_needed = True break return PlotComponent.do_layout(self, size, force) def _draw(self, *args, **kw): """ Draws the plot frame. Overrides PlotComponent and Container, explicitly calling the PlotComponent version of _draw(). """ PlotComponent._draw(self, *args, **kw) return def _dispatch_to_enable(self, event, suffix): """ Calls Enable-level event handlers. Overrides PlotComponent. """ Container.dispatch(self, event, suffix) return #------------------------------------------------------------------------ # Event handlers, properties #------------------------------------------------------------------------ def _bounds_changed(self, old, new): if self.container is not None: self.container._component_bounds_changed(self) self._layout_needed = True return def _bounds_items_changed(self, event): return self._bounds_changed(None, self.bounds) #------------------------------------------------------------------------ # Private methods #------------------------------------------------------------------------ def __getattr__(self, name): if name in self.slot_names: return self._frame_slots[name] else: raise AttributeError, "'%s' object has no attribute '%s'" % \ (self.__class__.__name__, name) def __setattr__(self, name, value): if name in self.slot_names: self.set_slot(name, value) else: super(BasePlotFrame, self).__setattr__(name, value) return ### Persistence ########################################################### # _pickles = ("_frame_slots", "_components", "fit_components", "fit_window") def post_load(self, path=None): super(BasePlotFrame, self).post_load(path) for slot in self._frame_slots.values(): slot.post_load(path) return # EOF chaco-4.5.0/chaco/base_xy_plot.py0000644000076600000240000006356112426452312017510 0ustar jrocherstaff00000000000000""" Defines the base class for XY plots. """ from __future__ import with_statement from math import sqrt from numpy import around, array, isnan, transpose # Enthought library imports from enable.api import black_color_trait from traits.api import Any, Array, Bool, Enum, Float, Instance, \ Property, Range # Local relative imports from abstract_mapper import AbstractMapper from abstract_plot_renderer import AbstractPlotRenderer from abstract_data_source import AbstractDataSource from array_data_source import ArrayDataSource from axis import PlotAxis from base import point_line_distance, reverse_map_1d from grid import PlotGrid from plot_label import PlotLabel class BaseXYPlot(AbstractPlotRenderer): """ Base class for simple X-vs-Y plots that consist of a single index data array and a single value data array. Subclasses handle the actual rendering, but this base class takes care of most of making sure events are wired up between mappers and data or screen space changes, etc. """ #------------------------------------------------------------------------ # Data-related traits #------------------------------------------------------------------------ # The data source to use for the index coordinate. index = Instance(ArrayDataSource) # The data source to use as value points. value = Instance(AbstractDataSource) # Screen mapper for index data. index_mapper = Instance(AbstractMapper) # Screen mapper for value data value_mapper = Instance(AbstractMapper) # Convenience properties that correspond to either index_mapper or # value_mapper, depending on the orientation of the plot. # Corresponds to either **index_mapper** or **value_mapper**, depending on # the orientation of the plot. x_mapper = Property # Corresponds to either **value_mapper** or **index_mapper**, depending on # the orientation of the plot. y_mapper = Property # Convenience property for accessing the index data range. index_range = Property # Convenience property for accessing the value data range. value_range = Property # The type of hit-testing that is appropriate for this renderer. # # * 'line': Computes Euclidean distance to the line between the # nearest adjacent points. # * 'point': Checks for adjacency to a marker or point. hittest_type = Enum("point", "line") #------------------------------------------------------------------------ # Appearance-related traits #------------------------------------------------------------------------ # The orientation of the index axis. orientation = Enum("h", "v") # Overall alpha value of the image. Ranges from 0.0 for transparent to 1.0 alpha = Range(0.0, 1.0, 1.0) #------------------------------------------------------------------------ # Convenience readonly properties for common annotations #------------------------------------------------------------------------ # Read-only property for horizontal grid. hgrid = Property # Read-only property for vertical grid. vgrid = Property # Read-only property for x-axis. x_axis = Property # Read-only property for y-axis. y_axis = Property # Read-only property for labels. labels = Property #------------------------------------------------------------------------ # Other public traits #------------------------------------------------------------------------ # Does the plot use downsampling? # This is not used right now. It needs an implementation of robust, fast # downsampling, which does not exist yet. use_downsampling = Bool(False) # Does the plot use a spatial subdivision structure for fast hit-testing? # This makes data updates slower, but makes hit-tests extremely fast. use_subdivision = Bool(False) # Overrides the default background color trait in PlotComponent. bgcolor = "transparent" # This just turns on a simple drawing of the X and Y axes... not a long # term solution, but good for testing. # Defines the origin axis color, for testing. origin_axis_color = black_color_trait # Defines a the origin axis width, for testing. origin_axis_width = Float(1.0) # Defines the origin axis visibility, for testing. origin_axis_visible = Bool(False) #------------------------------------------------------------------------ # Private traits #------------------------------------------------------------------------ # Are the cache traits valid? If False, new ones need to be compute. _cache_valid = Bool(False) # Cached array of (x,y) data-space points; regardless of self.orientation, # these points are always stored as (index_pt, value_pt). _cached_data_pts = Array # Cached array of (x,y) screen-space points. _cached_screen_pts = Array # Does **_cached_screen_pts** contain the screen-space coordinates # of the points currently in **_cached_data_pts**? _screen_cache_valid = Bool(False) # Reference to a spatial subdivision acceleration structure. _subdivision = Any #------------------------------------------------------------------------ # Abstract methods that subclasses must implement #------------------------------------------------------------------------ def _render(self, gc, points): """ Abstract method for rendering points. Parameters ---------- gc : graphics context Target for drawing the points points : List of Nx2 arrays Screen-space points to render """ raise NotImplementedError def _gather_points(self): """ Abstract method to collect data points that are within the range of the plot, and cache them. """ raise NotImplementedError def _downsample(self): """ Abstract method that gives the renderer a chance to downsample in screen space. """ # By default, this just does a mapscreen and returns the result raise NotImplementedError #------------------------------------------------------------------------ # Concrete methods below #------------------------------------------------------------------------ def __init__(self, **kwtraits): # Handling the setting/initialization of these traits manually because # they should be initialized in a certain order. kwargs_tmp = {"trait_change_notify": False} for trait_name in ("index", "value", "index_mapper", "value_mapper"): if trait_name in kwtraits: kwargs_tmp[trait_name] = kwtraits.pop(trait_name) self.set(**kwargs_tmp) AbstractPlotRenderer.__init__(self, **kwtraits) if self.index is not None: self.index.on_trait_change(self._either_data_changed, "data_changed") self.index.on_trait_change(self._either_metadata_changed, "metadata_changed") if self.index_mapper: self.index_mapper.on_trait_change(self._mapper_updated_handler, "updated") if self.value is not None: self.value.on_trait_change(self._either_data_changed, "data_changed") self.value.on_trait_change(self._either_metadata_changed, "metadata_changed") if self.value_mapper: self.value_mapper.on_trait_change(self._mapper_updated_handler, "updated") # If we are not resizable, we will not get a bounds update upon layout, # so we have to manually update our mappers if self.resizable == "": self._update_mappers() return def hittest(self, screen_pt, threshold=7.0, return_distance=False): """ Performs proximity testing between a given screen point and the plot. Parameters ---------- screen_pt : (x,y) A point to test. threshold : integer Optional maximum screen space distance (pixels) between *screen_pt* and the plot. return_distance : Boolean If True, also return the distance. Returns ------- If self.hittest_type is 'point', then this method returns the screen coordinates of the closest point on the plot as a tuple (x,y) If self.hittest_type is 'line', then this method returns the screen endpoints of the line segment closest to *screen_pt*, as ((x1,y1), (x2,y2)) If *screen_pt* does not fall within *threshold* of the plot, then this method returns None. If return_distance is True, return the (x, y, d), where d is the distance between the distance between the input point and the closest point (x, y), in screen coordinates. """ if self.hittest_type == "point": tmp = self.get_closest_point(screen_pt, threshold) elif self.hittest_type == "line": tmp = self.get_closest_line(screen_pt, threshold) else: raise ValueError("Unknown hittest type '%s'" % self.hittest_type) if tmp is not None: if return_distance: return tmp else: return tmp[:-1] else: return None def get_closest_point(self, screen_pt, threshold=7.0): """ Tests for proximity in screen-space. This method checks only data points, not the line segments connecting them; to do the latter use get_closest_line() instead. Parameters ---------- screen_pt : (x,y) A point to test. threshold : integer Optional maximum screen space distance (pixels) between *screen_pt* and the plot. If 0.0, then no threshold tests are performed, and the nearest point is returned. Returns ------- (x, y, distance) of a datapoint nearest to *screen_pt*. If no data points are within *threshold* of *screen_pt*, returns None. """ ndx = self.map_index(screen_pt, threshold) if ndx is not None: x = self.x_mapper.map_screen(self.index.get_data()[ndx]) y = self.y_mapper.map_screen(self.value.get_data()[ndx]) return (x, y, sqrt((x-screen_pt[0])**2 + (y-screen_pt[1])**2)) else: return None def get_closest_line(self, screen_pt, threshold=7.0): """ Tests for proximity in screen-space against lines connecting the points in this plot's dataset. Parameters ---------- screen_pt : (x,y) A point to test. threshold : integer Optional maximum screen space distance (pixels) between the line and the plot. If 0.0, then the method returns the closest line regardless of distance from the plot. Returns ------- (x1, y1, x2, y2, dist) of the endpoints of the line segment closest to *screen_pt*. The *dist* element is the perpendicular distance from *screen_pt* to the line. If there is only a single point in the renderer's data, then the method returns the same point twice. If no data points are within *threshold* of *screen_pt*, returns None. """ ndx = self.map_index(screen_pt, threshold=0.0) if ndx is None: return None index_data = self.index.get_data() value_data = self.value.get_data() x = self.x_mapper.map_screen(index_data[ndx]) y = self.y_mapper.map_screen(value_data[ndx]) # We need to find another index so we have two points; in the # even that we only have 1 point, just return that point. datalen = len(index_data) if datalen == 1: dist = (x, y, sqrt((x-screen_pt[0])**2 + (y-screen_pt[1])**2)) if (threshold == 0.0) or (dist <= threshold): return (x, y, x, y, dist) else: return None else: if (ndx == 0) or (screen_pt[0] >= x): ndx2 = ndx + 1 elif (ndx == datalen - 1) or (screen_pt[0] <= x): ndx2 = ndx - 1 x2 = self.x_mapper.map_screen(index_data[ndx2]) y2 = self.y_mapper.map_screen(value_data[ndx2]) dist = point_line_distance(screen_pt, (x,y), (x2,y2)) if (threshold == 0.0) or (dist <= threshold): return (x, y, x2, y2, dist) else: return None #------------------------------------------------------------------------ # AbstractPlotRenderer interface #------------------------------------------------------------------------ def map_screen(self, data_array): """ Maps an array of data points into screen space and returns it as an array. Implements the AbstractPlotRenderer interface. """ # data_array is Nx2 array if len(data_array) == 0: return [] x_ary, y_ary = transpose(data_array) sx = self.index_mapper.map_screen(x_ary) sy = self.value_mapper.map_screen(y_ary) if self.orientation == "h": return transpose(array((sx,sy))) else: return transpose(array((sy,sx))) def map_data(self, screen_pt, all_values=False): """ Maps a screen space point into the "index" space of the plot. Implements the AbstractPlotRenderer interface. If *all_values* is True, returns an array of (index, value) tuples; otherwise, it returns only the index values. """ x, y = screen_pt if self.orientation == 'v': x, y = y, x if all_values: return array((self.index_mapper.map_data(x), self.value_mapper.map_data(y))) else: return self.index_mapper.map_data(x) def map_index(self, screen_pt, threshold=2.0, outside_returns_none=True, index_only=False): """ Maps a screen space point to an index into the plot's index array(s). Implements the AbstractPlotRenderer interface. Parameters ---------- screen_pt : Screen space point threshold : float Maximum distance from screen space point to plot data point. A value of 0.0 means no threshold (any distance will do). outside_returns_none : bool If True, a screen space point outside the data range returns None. Otherwise, it returns either 0 (outside the lower range) or the last index (outside the upper range) index_only : bool If True, the threshold is measured on the distance between the index values, otherwise as Euclidean distance between the (x,y) coordinates. """ data_pt = self.map_data(screen_pt) if ((data_pt < self.index_mapper.range.low) or (data_pt > self.index_mapper.range.high)) and outside_returns_none: return None index_data = self.index.get_data() value_data = self.value.get_data() if len(value_data) == 0 or len(index_data) == 0: return None try: # find the closest point to data_pt in index_data ndx = reverse_map_1d(index_data, data_pt, self.index.sort_order) except IndexError: # if reverse_map raises this exception, it means that data_pt is # outside the range of values in index_data. if outside_returns_none: return None else: if data_pt < index_data[0]: return 0 else: return len(index_data) - 1 if threshold == 0.0: # Don't do any threshold testing return ndx x = index_data[ndx] y = value_data[ndx] if isnan(x) or isnan(y): return None # transform x,y in a 1x2 array, which is the preferred format of # map_screen. this makes it robust against differences in # the map_screen methods of logmapper and linearmapper # when passed a scalar xy = array([[x,y]]) sx, sy = self.map_screen(xy).T if index_only and (threshold == 0.0 or screen_pt[0]-sx < threshold): return ndx elif ((screen_pt[0]-sx)**2 + (screen_pt[1]-sy)**2 < threshold*threshold): return ndx else: return None def get_screen_points(self): """Returns the currently visible screen-space points. Intended for use with overlays. """ self._gather_points() if self.use_downsampling: # The BaseXYPlot implementation of _downsample doesn't actually # do any downsampling. return self._downsample() else: return self.map_screen(self._cached_data_pts) #------------------------------------------------------------------------ # PlotComponent interface #------------------------------------------------------------------------ def _draw_plot(self, gc, view_bounds=None, mode="normal"): """ Draws the 'plot' layer. """ self._draw_component(gc, view_bounds, mode) return def _draw_component(self, gc, view_bounds=None, mode="normal"): # This method should be folded into self._draw_plot(), but is here for # backwards compatibilty with non-draw-order stuff. pts = self.get_screen_points() self._render(gc, pts) return def _draw_default_axes(self, gc): if not self.origin_axis_visible: return with gc: gc.set_stroke_color(self.origin_axis_color_) gc.set_line_width(self.origin_axis_width) gc.set_line_dash(None) for range in (self.index_mapper.range, self.value_mapper.range): if (range.low < 0) and (range.high > 0): if range == self.index_mapper.range: dual = self.value_mapper.range data_pts = array([[0.0,dual.low], [0.0, dual.high]]) else: dual = self.index_mapper.range data_pts = array([[dual.low,0.0], [dual.high,0.0]]) start,end = self.map_screen(data_pts) start = around(start) end = around(end) gc.move_to(int(start[0]), int(start[1])) gc.line_to(int(end[0]), int(end[1])) gc.stroke_path() return def _post_load(self): super(BaseXYPlot, self)._post_load() self._update_mappers() self.invalidate_draw() self._cache_valid = False self._screen_cache_valid = False return def _update_subdivision(self): return #------------------------------------------------------------------------ # Properties #------------------------------------------------------------------------ def _get_index_range(self): return self.index_mapper.range def _set_index_range(self, val): self.index_mapper.range = val def _get_value_range(self): return self.value_mapper.range def _set_value_range(self, val): self.value_mapper.range = val def _get_x_mapper(self): if self.orientation == "h": return self.index_mapper else: return self.value_mapper def _get_y_mapper(self): if self.orientation == "h": return self.value_mapper else: return self.index_mapper def _get_hgrid(self): for obj in self.underlays+self.overlays: if isinstance(obj, PlotGrid) and obj.orientation=="horizontal": return obj else: return None def _get_vgrid(self): for obj in self.underlays+self.overlays: if isinstance(obj, PlotGrid) and obj.orientation=="vertical": return obj else: return None def _get_x_axis(self): for obj in self.underlays+self.overlays: if isinstance(obj, PlotAxis) and obj.orientation in ("bottom", "top"): return obj else: return None def _get_y_axis(self): for obj in self.underlays+self.overlays: if isinstance(obj, PlotAxis) and obj.orientation in ("left", "right"): return obj else: return None def _get_labels(self): labels = [] for obj in self.underlays+self.overlays: if isinstance(obj, PlotLabel): labels.append(obj) return labels #------------------------------------------------------------------------ # Event handlers #------------------------------------------------------------------------ def _update_mappers(self): x_mapper = self.index_mapper y_mapper = self.value_mapper if self.orientation == "v": x_mapper, y_mapper = y_mapper, x_mapper x = self.x x2 = self.x2 y = self.y y2 = self.y2 if "left" in self.origin: x_mapper.screen_bounds = (x, x2) else: x_mapper.screen_bounds = (x2, x) if "bottom" in self.origin: y_mapper.screen_bounds = (y, y2) else: y_mapper.screen_bounds = (y2, y) self.invalidate_draw() self._cache_valid = False self._screen_cache_valid = False def _bounds_changed(self, old, new): super(BaseXYPlot, self)._bounds_changed(old, new) self._update_mappers() def _bounds_items_changed(self, event): super(BaseXYPlot, self)._bounds_items_changed(event) self._update_mappers() def _position_changed(self): self._update_mappers() def _position_items_changed(self): self._update_mappers() def _orientation_changed(self): self._update_mappers() def _index_changed(self, old, new): if old is not None: old.on_trait_change(self._either_data_changed, "data_changed", remove=True) old.on_trait_change(self._either_metadata_changed, "metadata_changed", remove=True) if new is not None: new.on_trait_change(self._either_data_changed, "data_changed") new.on_trait_change(self._either_metadata_changed, "metadata_changed") self._either_data_changed() return def _either_data_changed(self): self.invalidate_draw() self._cache_valid = False self._screen_cache_valid = False self.request_redraw() return def _either_metadata_changed(self): # By default, don't respond to metadata change events. pass def _value_changed(self, old, new): if old is not None: old.on_trait_change(self._either_data_changed, "data_changed", remove=True) old.on_trait_change(self._either_metadata_changed, "metadata_changed", remove=True) if new is not None: new.on_trait_change(self._either_data_changed, "data_changed") new.on_trait_change(self._either_metadata_changed, "metadata_changed") self._either_data_changed() return def _origin_changed(self, old, new): # origin switch from left to right or vice versa? if old.split()[1] != new.split()[1]: xm = self.x_mapper xm.low_pos, xm.high_pos = xm.high_pos, xm.low_pos # origin switch from top to bottom or vice versa? if old.split()[0] != new.split()[0]: ym = self.y_mapper ym.low_pos, ym.high_pos = ym.high_pos, ym.low_pos self.invalidate_draw() self._screen_cache_valid = False return def _index_mapper_changed(self, old, new): self._either_mapper_changed(self, "index_mapper", old, new) if self.orientation == "h": self.trait_property_changed("x_mapper", old, new) else: self.trait_property_changed("y_mapper", old, new) return def _value_mapper_changed(self, old, new): self._either_mapper_changed(self, "value_mapper", old, new) if self.orientation == "h": self.trait_property_changed("y_mapper", old, new) else: self.trait_property_changed("x_mapper", old, new) return def _either_mapper_changed(self, obj, name, old, new): if old is not None: old.on_trait_change(self._mapper_updated_handler, "updated", remove=True) if new is not None: new.on_trait_change(self._mapper_updated_handler, "updated") self.invalidate_draw() self._screen_cache_valid = False return def _mapper_updated_handler(self): self._cache_valid = False self._screen_cache_valid = False self.invalidate_draw() self.request_redraw() return def _visible_changed(self, old, new): if new: self._layout_needed = True def _bgcolor_changed(self): self.invalidate_draw() def _use_subdivision_changed(self, old, new): if new: self._set_up_subdivision() return #------------------------------------------------------------------------ # Persistence #------------------------------------------------------------------------ def __getstate__(self): state = super(BaseXYPlot,self).__getstate__() for key in ['_cache_valid', '_cached_data_pts', '_screen_cache_valid', '_cached_screen_pts']: if state.has_key(key): del state[key] return state def __setstate__(self, state): super(BaseXYPlot, self).__setstate__(state) if self.index is not None: self.index.on_trait_change(self._either_data_changed, "data_changed") if self.value is not None: self.value.on_trait_change(self._either_data_changed, "data_changed") self.invalidate_draw() self._cache_valid = False self._screen_cache_valid = False self._update_mappers() return # EOF chaco-4.5.0/chaco/candle_plot.py0000644000076600000240000001440112426452312017271 0ustar jrocherstaff00000000000000 from __future__ import with_statement # Major library imports from numpy import array, compress, concatenate, searchsorted # Enthought library imports from traits.api import Instance, Property # Chaco imports from abstract_data_source import AbstractDataSource from base_candle_plot import BaseCandlePlot def broaden(mask): """ Takes a 1D boolean mask array and returns a copy with all the non-zero runs widened by 1. """ if len(mask) < 2: return mask # Note: the order in which these operations are performed is important. # Modifying newmask in-place with the |= operator only works for if # newmask[:-1] is the L-value. newmask = concatenate(([False], mask[1:] | mask[:-1])) newmask[:-1] |= mask[1:] return newmask class CandlePlot(BaseCandlePlot): """ A plot consisting of a filled bar with an optional centerline and stems extending to extrema. Usually used to represent some statistics on bins of data, with the centerline representing the mean, the bar extents representing +/- 1 standard dev or 10th/90th percentiles, and the stems extents representing the minimum and maximum samples. The values in the **index** datasource indicate the centers of the bins; the widths of the bins are *not* specified in data space, and are determined by the minimum space between adjacent index values. """ #------------------------------------------------------------------------ # Data-related traits #------------------------------------------------------------------------ # The minimum values at each index point. If None, then no stem and no # endcap line will be drawn below each bar. min_values = Instance(AbstractDataSource) # The "lower" extent of the "bar", i.e. the value closest to the # corresponding value in min_values at each index. bar_min = Instance(AbstractDataSource) # Values that appear inside the bar, between bar_min and bar_max. These # Are usually mean or median values, and are rendered with a solid line # of a different color than the bar fill color. This can be None. center_values = Instance(AbstractDataSource) # The "upper" extent of the "bar", i.e. the value closest to the # corresponding value in max_values at each index. bar_max = Instance(AbstractDataSource) # The maximum value at each index point. If None, then no stem and no # endcap line will be drawn above each bar. max_values = Instance(AbstractDataSource) value = Property def map_data(self, screen_pt, all_values=True): """ Maps a screen space point into the "index" space of the plot. Overrides the BaseXYPlot implementation, and always returns an array of (index, value) tuples. """ x, y = screen_pt if self.orientation == 'v': x, y = y, x return array((self.index_mapper.map_data(x), self.value_mapper.map_data(y))) def map_index(self, screen_pt, threshold=0.0, outside_returns_none=True, index_only = True): if not index_only: raise NotImplementedError("Candle Plots only support index_only map_index()") if len(screen_pt) == 0: return None # Find the closest index point using numpy index_data = self.index.get_data() if len(index_data) == 0: return None target_data = self.index_mapper.map_data(screen_pt[0]) index = searchsorted(index_data, [target_data])[0] if index == len(index_data): index -= 1 # Bracket index and map those points to screen space, then # compute the distance if index > 0: lower = index_data[index-1] upper = index_data[index] screen_low, screen_high = self.index_mapper.map_screen(array([lower, upper])) # Find the closest index low_dist = abs(screen_pt[0] - screen_low) high_dist = abs(screen_pt[0] - screen_high) if low_dist < high_dist: index = index - 1 dist = low_dist else: dist = high_dist # Determine if we need to check the threshold if threshold > 0 and dist >= threshold: return None else: return index else: screen = self.index_mapper.map_screen(index_data[0]) if threshold > 0 and abs(screen - screen_pt[0]) >= threshold: return None else: return index def _gather_points(self): index = self.index.get_data() mask = broaden(self.index_range.mask_data(index)) if not mask.any(): self._cached_data_pts = [] self._cache_valid = True return data_pts = [compress(mask, index)] for v in (self.min_values, self.bar_min, self.center_values, self.bar_max, self.max_values): if v is None or len(v.get_data()) == 0: data_pts.append(None) else: data_pts.append(compress(mask, v.get_data())) self._cached_data_pts = data_pts self._cache_valid = True def _draw_plot(self, gc, view_bounds=None, mode="normal"): self._gather_points() if len(self._cached_data_pts) == 0: return index = self.index_mapper.map_screen(self._cached_data_pts[0]) if len(index) == 0: return vals = [] for v in self._cached_data_pts[1:]: if v is None: vals.append(None) else: vals.append(self.value_mapper.map_screen(v)) # Compute lefts and rights from self.index, which represents bin # centers. if len(index) == 1: width = 5.0 else: width = (index[1:] - index[:-1]).min() / 2.5 left = index - width right = index + width with gc: gc.clip_to_rect(self.x, self.y, self.width, self.height) self._render(gc, left, right, *vals) def _get_value(self): if self.center_values is not None: return self.center_values elif self.bar_min is not None: return self.bar_min elif self.bar_max is not None: return self.bar_max chaco-4.5.0/chaco/chaco_plot_container_editor.py0000644000076600000240000000067712426452312022542 0ustar jrocherstaff00000000000000""" Deprecated alias for ComponentEditor. """ import warnings from enable.component_editor import ComponentEditor class PlotContainerEditor(ComponentEditor): """ Deprecated alias for ComponentEditor. """ def __init__(self, *args, **kwds): super(PlotContainerEditor, self).__init__(*args, **kwds) warnings.warn("DEPRECATED: Use enable.component_editor" ".ComponentEditor instead.", DeprecationWarning) chaco-4.5.0/chaco/chaco_plot_editor.py0000644000076600000240000003703112426452312020472 0ustar jrocherstaff00000000000000""" Traits UI editor for WX, based on the Chaco1 PlotEditor in traits.ui.wx.plot_editor. """ # Enthought library imports from traits.etsconfig.api import ETSConfig from enable.api import black_color_trait, LineStyle, ColorTrait,\ white_color_trait, MarkerTrait, Window from enable.trait_defs.ui.api import RGBAColorEditor from kiva.trait_defs.kiva_font_trait import KivaFont from traits.api import Enum, Str, Range, Tuple, \ Bool, Trait, Int, Any, Property from traitsui.api import Item from traitsui.editor_factory import EditorFactory # Toolkit dependent imports from traitsui.toolkit import toolkit_object Editor = toolkit_object('editor:Editor') # Local relative imports from axis import PlotAxis from plot_containers import OverlayPlotContainer from plot_factory import create_line_plot, create_scatter_plot, \ add_default_grids, add_default_axes from plot_label import PlotLabel # Somewhat unorthodox... from chaco.tools.api import PanTool, ZoomTool #------------------------------------------------------------------------------- # Trait definitions: #------------------------------------------------------------------------------- # Range of values for an axis. AxisRange = Tuple( ( 0.0, 1.0, 0.01 ), labels = [ 'Low', 'High', 'Step' ], cols = 3 ) # Range of axis bounds. AxisBounds = Tuple( ( 0.0, 1.0 ), labels = [ 'Min', 'Max' ], cols = 2 ) # Range for the height and width for the plot widget. PlotSize = Range( 50, 1000, 180 ) # Range of plot line weights. LineWeight = Range( 1, 9, 3 ) # The color editor to use for various color traits. color_editor = RGBAColorEditor() USE_DATA_UPDATE = 1 class ChacoPlotItem(Item): """ A Traits UI Item for a Chaco plot, for use in Traits UI Views. NOTE: ComponentEditor is preferred over this class, as it is more flexible. """ # Name of the trait that references the index data source. index = Str # Name of the trait that references the value data source. value = Str # Title of the plot (overlaid on the plot container). title = Str("Plot Editor") # Bounds of the x-axis, used if **x_auto** is False. x_bounds = AxisBounds # Set the x-axis bounds automatically? x_auto = Bool(True) # Bounds of the y-axis, used if **y_auto** is False. y_bounds = AxisBounds # Set the y-axis bounds automatically? y_auto = Bool(True) # The orientation of the index axis. orientation = Enum("h", "v") # If these are None, then the index/value trait names are used # Label of the x-axis; if None, the **index** name is used. x_label = Trait(None, None, Str) # Name of the trait on the object containing the label of the x-axis. # This takes precedence over **x_label**. x_label_trait = Trait(None, None, Str) # Font for the label of the x-axis. x_label_font = KivaFont("modern 10") # Color of the label of the x-axis. x_label_color = black_color_trait # Label of the y-axis; if None, the **value** name is used. y_label = Trait(None, None, Str) # Name of the trait on the object containing the label of the y-axis. # This takes precedence over **y_label**. y_label_trait = Trait(None, None, Str) # Font for the label of the y-axis. y_label_font = KivaFont("modern 10") # Color of the label of the y-axis. y_label_color = black_color_trait # General plot properties # Foreground olor of the plot. color = ColorTrait("blue") # Background color of the plot. bgcolor = white_color_trait # Background color of the plot (deprecated). bg_color = Property # backwards compatibility; deprecated # Color of the background padding. padding_bg_color = ColorTrait("sys_window") # Border properties # Width of the plot border border_width = Int(1) # Is the border visible? border_visible = Bool(False) # Line style of the border. border_dash = LineStyle # Color of the border. border_color = black_color_trait # The type of the plot. type = Enum("line", "scatter") # The type of the plot as a string. type_trait = Str # plot-specific properties. These might not apply to all plot types. # Type of marker (for plots that use markers). marker = MarkerTrait # Size of marker (for plots that use markers). marker_size = Int(4) # Marker outline color (for plots that user markers). outline_color = black_color_trait def __init__(self, index, value, type="line", **traits): self.index = index self.value = value self.type = type self.name = index super(ChacoPlotItem, self).__init__(**traits) self.editor = ChacoEditorFactory() self.editor.plotitem = self return def _set_bg_color(self, val): self.bgcolor = val def _get_bg_color(self): return self.bgcolor class ChacoEditorFactory ( EditorFactory ): """ Editor factory for plot editors. """ #--------------------------------------------------------------------------- # Trait definitions: #--------------------------------------------------------------------------- # Width of the plot editor. width = PlotSize # Height of the plot editor. height = PlotSize # The ChacoPlotItem associated with this factory. plotitem = Any #--------------------------------------------------------------------------- # 'Editor' factory methods: #--------------------------------------------------------------------------- def simple_editor ( self, ui, object, name, description, parent ): return ChacoPlotEditor( parent, factory = self, ui = ui, object = object, name = name, description = description ) def text_editor ( self, ui, object, name, description, parent ): return ChacoPlotEditor( parent, factory = self, ui = ui, object = object, name = name, description = description ) def readonly_editor ( self, ui, object, name, description, parent ): return ChacoPlotEditor( parent, factory = self, ui = ui, object = object, name = name, description = description ) class ChacoPlotEditor ( Editor ): """ Traits UI editor for displaying trait values in a Chaco plot. """ #--------------------------------------------------------------------------- # Finishes initializing the editor by creating the underlying toolkit # widget: #--------------------------------------------------------------------------- def init ( self, parent ): """ Finishes initializing the editor by creating the underlying toolkit widget. """ factory = self.factory plotitem = factory.plotitem container = OverlayPlotContainer(padding = 50, fill_padding = True, bgcolor = plotitem.padding_bg_color, use_backbuffer=True) if plotitem.title != '': container.overlays.append(PlotLabel(plotitem.title, component=container, overlay_position="top")) self._container = container window = Window(parent, component = container) # FIXME: Toolkit specifc code here. The AbstractWindow should have a # 'set size' method as part of its API. self.control = control = window.control if ETSConfig.toolkit == 'wx': control.SetSize((factory.width, factory.height)) elif ETSConfig.toolkit == 'qt4': control.resize(factory.width, factory.height) else: raise NotImplementedError # Attach listeners to the object's traits appropriately so we can # update the plot when they change. For the _update_axis_grids() # callback, we have to wrap it in a lambda to keep traits from # inferring the calling convention based on introspecting the argument # list. object = self.object if USE_DATA_UPDATE == 1: for name in (plotitem.index, plotitem.value): object.on_trait_change( self._update_data, name) for name in (plotitem.x_label_trait, plotitem.y_label_trait): object.on_trait_change(lambda s: self._update_axis_grids(), name) if plotitem.type_trait not in ("", None): object.on_trait_change(self.update_editor, plotitem.type_trait) return #--------------------------------------------------------------------------- # Disposes of the contents of an editor: #--------------------------------------------------------------------------- def dispose(self): """ Disposes of the contents of the editor. """ object = self.object plotitem = self.factory.plotitem if USE_DATA_UPDATE == 1: for name in (plotitem.index, plotitem.value): object.on_trait_change( self._update_data, name, remove = True ) for name in (plotitem.type_trait,): object.on_trait_change( self.update_editor, name, remove = True ) self._destroy_plot() super(ChacoPlotEditor, self).dispose() def _destroy_plot(self): if self._container and self._plot: plot = self._plot del plot.index._data del plot.index._cached_mask del plot.value._data del plot.value._cached_mask self._container.remove(plot) self._plot = None plot.index = None plot.value = None return #--------------------------------------------------------------------------- # Updates the editor when the object trait changes externally to the editor: #--------------------------------------------------------------------------- def update_editor(self): """ Updates the editor when the object trait changes externally to the editor. """ factory = self.factory if factory is None: return plotitem = factory.plotitem # Remove the old plot if self._plot is not None: self._destroy_plot() try: x_values = getattr(self.object, plotitem.index) y_values = getattr(self.object, plotitem.value) except: self._container.request_redraw() return if plotitem.type_trait != "": plot_type = getattr(self.object, plotitem.type_trait) else: plot_type = plotitem.type if plotitem.x_auto == True: index_bounds = None else: index_bounds = plotitem.x_bounds if plotitem.y_auto == True: value_bounds = None else: value_bounds = plotitem.y_bounds # Class-level attribute mapping different plot_type strings to methods for # creating different types of plots plot_creator_map = { "line": self._create_line_plot, "scatter": self._create_scatter_plot } if plot_type in plot_creator_map.keys(): plot = plot_creator_map[plot_type](plotitem, (x_values, y_values), index_bounds = index_bounds, value_bounds = value_bounds, orientation = plotitem.orientation) else: raise RuntimeError, "Unknown plot type '%s' in ChacoPlotEditor." % plot_type self._set_basic_properties(plot, plotitem) self._add_axis_grids(plot, plotitem) self._plot = plot self._container.add(plot) self._container.request_redraw() return def _update_data(self): """ Updates the editor when the object trait changes externally to the editor. """ if self._plot is None: self.update_editor() else: x_values = getattr(self.object, self.factory.plotitem.index) y_values = getattr(self.object, self.factory.plotitem.value) self._plot.index.set_data(x_values) self._plot.value.set_data(y_values) def _set_basic_properties(self, plot, plotitem): for attr in ("color", "bgcolor", "border_visible", "border_width", "border_dash", "border_color"): setattr(plot, attr, getattr(plotitem, attr)) return def _create_line_plot(self, plotitem, values, **kwargs): plot = create_line_plot(values, **kwargs) return plot def _create_scatter_plot(self, plotitem, values, **kwargs): plot = create_scatter_plot(values, **kwargs) for attr in ("marker", "marker_size", "outline_color"): setattr(plot, attr, getattr(plotitem, attr)) return plot def _add_axis_grids(self, new_plot, plotitem): value_axis, index_axis = add_default_axes(new_plot, orientation=plotitem.orientation) add_default_grids(new_plot) new_plot.tools.append(PanTool(new_plot)) zoom = ZoomTool(component=new_plot, tool_mode="box", always_on=False) new_plot.overlays.append(zoom) # Update the titles and labels self._update_axis_grids(new_plot, plotitem) def _update_axis_grids(self, plot=None, plotitem=None): if self.factory is None: return if plot is None: if self._plot is None: return else: plot = self._plot if plotitem is None: plotitem = self.factory.plotitem if plotitem.x_label_trait is not None: htitle = getattr(self.object, plotitem.x_label_trait) elif plotitem.x_label is not None: htitle = plotitem.x_label else: htitle = plotitem.index if plotitem.y_label_trait is not None: vtitle = getattr(self.object, plotitem.y_label_trait) elif plotitem.y_label is not None: vtitle = plotitem.y_label else: vtitle = plotitem.value if plotitem.orientation == "v": htitle, vtitle = vtitle, htitle plot.x_axis.title = htitle plot.y_axis.title = vtitle # This is sort of crappy.. since we are using BaseXYPlots and not # Plot/DataViews, we actually can't easily get references to the plot's # index and value axes. So we have to search through the underlays for # PlotAxis instances whose ranges match the index and value ranges. for axis in plot.underlays + plot.overlays: if isinstance(axis, PlotAxis) and axis.mapper.range is plot.index_range: axis.title_font = plotitem.x_label_font axis.title_color = plotitem.x_label_color for axis in plot.underlays + plot.overlays: if isinstance(axis, PlotAxis) and axis.mapper.range is plot.value_range: axis.title_font = plotitem.y_label_font axis.title_color = plotitem.y_label_color plot.request_redraw() return # EOF chaco-4.5.0/chaco/chaco_traits.py0000644000076600000240000000143412426452312017452 0ustar jrocherstaff00000000000000""" Defines various traits that are used in many places in Chaco. """ # Enthought library imports from traits.api import Enum #---------------------------------------------------------------------------- # Box positioning traits: used to specify positions of boxes relative to # one another. Generally used for layout. #---------------------------------------------------------------------------- box_edge_enum = Enum("left", "right", "top", "bottom") # Values correspond to: top, bottom, left, right, top left, top right, bottom # left, bottom right box_position_enum = Enum("T", "B", "L", "R", "TL", "TR", "BL", "BR") # For backwards compatibility, import LineStyle & LineStyleEditor from enable. # (They used to be defined here.) from enable.api import LineStyle, LineStyleEditor # EOF chaco-4.5.0/chaco/chaco_version.py0000644000076600000240000000145412426452312017633 0ustar jrocherstaff00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2005, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # Thanks for using Enthought open source! # # Author: Enthought, Inc. # Description: #------------------------------------------------------------------------------ """ Defines version numbering for the Chaco package. """ major = 2 minor = 0 micro = 9 version = "%s.%s.%s" % (major, minor, micro) release_level = "beta" branch = "" revision = version chaco-4.5.0/chaco/cmap_image_plot.py0000644000076600000240000001564212426462410020135 0ustar jrocherstaff00000000000000# # (C) Copyright 2013 Enthought, Inc., Austin, TX # All right reserved. # # This file is open source software distributed according to the terms in # LICENSE.txt # from numpy import zeros # Enthought library imports. from traits.api import Any, Bool, Float, Instance, Property, Tuple # Local relative imports from image_plot import ImagePlot from abstract_colormap import AbstractColormap from speedups import apply_selection_fade class CMapImagePlot(ImagePlot): """ Colormapped image plot. Takes a value data object whose elements are scalars, and renders them as a colormapped image. """ # TODO: Modify ImageData to explicitly support scalar value arrays #------------------------------------------------------------------------ # Data-related traits #------------------------------------------------------------------------ # Maps from scalar data values in self.data.value to color tuples value_mapper = Instance(AbstractColormap) # Convenience property for value_mapper as color_mapper color_mapper = Property # Convenience property for accessing the data range of the mapper. value_range = Property # alpha value to use to fade out unselected data points when there is an # active selection fade_alpha = Float(0.3) #fade_background = Tuple((255,255,255)) # RGB color to use to fade out unselected points. fade_background = Tuple((0,0,0)) # whether to pre-compute the full colormapped RGB(A) image cache_full_map = Bool(True) #------------------------------------------------------------------------ # Private Traits #------------------------------------------------------------------------ # Is the mapped image valid? _mapped_image_cache_valid = Bool(False) # Cache of the fully mapped RGB(A) image. _cached_mapped_image = Any #------------------------------------------------------------------------ # Public methods #------------------------------------------------------------------------ def __init__(self, **kwargs): super(CMapImagePlot, self).__init__(**kwargs) if self.value_mapper: self.value_mapper.on_trait_change(self._update_value_mapper, "updated") if self.value: self.value.on_trait_change(self._update_selections, "metadata_changed") def set_value_selection(self, val): """ Sets a range of values in the value data source as selected. """ if val is not None: low, high = val data = self.value.get_data() new_mask = (data>=low) & (data<=high) self.value.metadata["selection_masks"] = [new_mask] else: del self.value.metadata["selection_masks"] self._update_selections() #------------------------------------------------------------------------ # Base2DPlot interface #------------------------------------------------------------------------ def _render(self, gc): """ Ensures that the cached image is valid. Called before _render() is called. Implements the Base2DPlot interface. """ if not self._mapped_image_cache_valid: if 'selection_masks' in self.value.metadata: self._compute_cached_image(self.value.metadata['selection_masks']) else: self._compute_cached_image() ImagePlot._render(self, gc) #------------------------------------------------------------------------ # Private methods #------------------------------------------------------------------------ def _cmap_values(self, data, selection_masks=None): """ Maps the data to RGB(A) with optional selection masks overlayed """ # get the RGBA values from the color map as uint8 mapped_image = self.value_mapper.map_uint8(data) if selection_masks is not None: # construct a composite mask if len(selection_masks) > 0: mask = zeros(mapped_image.shape[:2], dtype=bool) for m in selection_masks: mask = mask | m else: mask = zeros(self._cached_mapped_image.shape[:2], dtype=bool) # Apply the selection fade, from speedups.py apply_selection_fade(mapped_image, mask, self.fade_alpha, self.fade_background) return mapped_image def _compute_cached_image(self, selection_masks=None): """ Updates the cached image. """ if self.cache_full_map: if not self._mapped_image_cache_valid: self._cached_mapped_image = self._cmap_values(self.value.data, selection_masks) self._mapped_image_cache_valid = True mapped_value = self._cached_mapped_image ImagePlot._compute_cached_image(self, mapped_value) else: self._mapped_image_cache_valid = True ImagePlot._compute_cached_image(self, self.value.data, mapper=lambda data: self._cmap_values(data)) def _update_value_mapper(self): self._mapped_image_cache_valid = False self._image_cache_valid = False self.invalidate_and_redraw() def _update_selections(self): self._mapped_image_cache_valid = False self._image_cache_valid = False self.invalidate_and_redraw() #------------------------------------------------------------------------ # Properties #------------------------------------------------------------------------ def _get_value_range(self): return self.value_mapper.range def _set_value_range(self, val): self.value_mapper.range = val def _get_color_mapper(self): return self.value_mapper def _set_color_mapper(self, val): self.value_mapper = val #------------------------------------------------------------------------ # Event handlers #------------------------------------------------------------------------ def _value_mapper_changed(self, old, new): if old is not None: old.on_trait_change(self._update_value_mapper, "updated", remove=True) if new is not None: new.on_trait_change(self._update_value_mapper, "updated") if old and new: if new.range is None and old.range is not None: new.range = old.range self._update_value_mapper() def _value_data_changed_fired(self): super(CMapImagePlot, self)._value_data_changed_fired() self._mapped_image_cache_valid = False return def _index_data_changed_fired(self): super(CMapImagePlot, self)._index_data_changed_fired() self._mapped_image_cache_valid = False return def _cache_full_map_changed(self): self._mapped_image_cache_valid = False chaco-4.5.0/chaco/color_bar.py0000644000076600000240000002402112426452312016746 0ustar jrocherstaff00000000000000""" Defines the ColorBar class. """ from __future__ import with_statement # Major library imports from numpy import array, arange, ascontiguousarray, ones, transpose, uint8 # Enthought library imports from traits.api import Any, Bool, Enum, Instance, Property, \ cached_property, on_trait_change from kiva.image import GraphicsContext # Local imports from base_xy_plot import BaseXYPlot from abstract_plot_renderer import AbstractPlotRenderer from abstract_mapper import AbstractMapper from array_data_source import ArrayDataSource from grid import PlotGrid from axis import PlotAxis class ColorBar(AbstractPlotRenderer): """ A color bar for a color-mapped plot. """ # Screen mapper for index data. index_mapper = Instance(AbstractMapper) # Screen mapper for color data color_mapper = Property #Instance(ColorMapper) # Screen mapper for value data (synonym for color_mapper) value_mapper = Property(depends_on='color_mapper') # Optional index data source for generic tools to attach metadata to. index = Property # Optional color-mapped plot that this color bar references. If specified, # the plot must have a **color_mapper** attribute. plot = Any # Is there a visible grid on the colorbar? grid_visible = Bool(True) # Is there a visible axis on the colorbar? axis_visible = Bool(True) # Corresponds to either **index_mapper** or None, depending on # the orientation of the plot. x_mapper = Property # Corresponds to either **index_mapper** or None, depending on # the orientation of the plot. y_mapper = Property #------------------------------------------------------------------------ # Override default values of inherited traits #------------------------------------------------------------------------ # The border is visible (overrides enable.Component). border_visible = True # The orientation of the index axis. orientation = Enum('v', 'h') # Should the bar go left-to-right or bottom-to-top (normal) or the reverse? direction = Enum('normal', 'flipped') # Overrides the default background color trait in PlotComponent. bgcolor = 'transparent' # Draw layers in "draw order" use_draw_order = True # Default width is 40 pixels (overrides enable.CoordinateBox) width = 40 # Faux origin for the axis to look at origin = Enum('bottom left', 'top left', 'bottom right', 'top right') #------------------------------------------------------------------------ # Private attributes #------------------------------------------------------------------------ # The grid _grid = Instance(PlotGrid) # The axis _axis = Instance(PlotAxis) # Shadow attribute for color_mapper _color_mapper = Any # Shadow attribute for index _index = Instance(ArrayDataSource, args=()) def __init__(self, *args, **kw): """ In creating an instance, this method ensures that the grid and the axis are created before setting their visibility. """ grid_visible = kw.pop("grid_visible", True) axis_visible = kw.pop("axis_visible", True) super(ColorBar, self).__init__(*args, **kw) if self.orientation == 'h': if self.direction == 'normal': self.origin = 'bottom left' else: self.origin = 'bottom right' grid_orientation = 'vertical' axis_orientation = 'bottom' else: if self.direction == 'normal': self.origin = 'bottom left' else: self.origin = 'top left' grid_orientation = 'horizontal' axis_orientation = 'left' self._grid = PlotGrid(orientation=grid_orientation, mapper=self.index_mapper, component=self) self._axis = PlotAxis(orientation=axis_orientation, mapper=self.index_mapper, component=self) self.overlays.append(self._grid) self.overlays.append(self._axis) # Now that we have a grid and an axis, we can safely set the visibility self.grid_visible = grid_visible self.axis_visible = axis_visible return def _draw_plot(self, gc, view_bounds=None, mode='normal'): """ Draws the 'plot' layer. """ self._update_mappers() with gc: if self.orientation == 'h': perpendicular_dim = 1 axis_dim = 0 else: perpendicular_dim = 0 axis_dim = 1 mapper = self.index_mapper scrn_points = arange(mapper.low_pos, mapper.high_pos+1) # Get the data values associated with the list of screen points. if mapper.range.low == mapper.range.high: # LogMapper.map_data() returns something unexpected if low==high, # so we'll handle that case here. data_points = array([mapper.range.high]) else: data_points = mapper.map_data(scrn_points) if self.direction == 'flipped': data_points = data_points[::-1] # Get the colors associated with the data points. colors = self.color_mapper.map_screen(data_points) img = self._make_color_image(colors, self.bounds[perpendicular_dim], self.orientation, self.direction) gc.draw_image(img, (self.x, self.y, self.width, self.height)) def _make_color_image(self, color_values, width, orientation, direction): """ Returns an image graphics context representing the array of color values (Nx3 or Nx4). The *width* parameter is the width of the colorbar, and *orientation* is the orientation of the plot. """ bmparray = ones((width, color_values.shape[0], color_values.shape[1]))* color_values * 255 if orientation == "v": bmparray = ascontiguousarray(transpose(bmparray, axes=(1,0,2))[::-1]) bmparray = bmparray.astype(uint8) img = GraphicsContext(bmparray, "rgba32") return img #------------------------------------------------------------------------ # Trait events #------------------------------------------------------------------------ def _update_mappers(self): if not self.index_mapper or not self.color_mapper: return if self.orientation == 'h' and 'left' in self.origin: self.index_mapper.low_pos = self.x self.index_mapper.high_pos = self.x2 elif self.orientation == 'h' and 'right' in self.origin: self.index_mapper.low_pos = self.x2 self.index_mapper.high_pos = self.x elif self.orientation == 'v' and 'bottom' in self.origin: self.index_mapper.low_pos = self.y self.index_mapper.high_pos = self.y2 elif self.orientation == 'v' and 'top' in self.origin: self.index_mapper.low_pos = self.y2 self.index_mapper.high_pos = self.y self.index_mapper.range = self.color_mapper.range def _bounds_changed(self, old, new): super(ColorBar, self)._bounds_changed(old, new) self._update_mappers() def _bounds_items_changed(self, event): super(ColorBar, self)._bounds_items_changed(event) self._update_mappers() def _position_changed(self, old, new): super(ColorBar, self)._position_changed(old, new) self._update_mappers() def _position_items_changed(self, event): super(ColorBar, self)._position_items_changed(event) self._update_mappers() def _updated_changed_for_index_mapper(self): self._update_mappers() def _updated_changed_for_color_mapper(self): self._update_mappers() @on_trait_change('[index_mapper,color_mapper].+') def _either_mapper_changed(self): self.invalidate_draw() self.request_redraw() def _index_mapper_changed(self): # Keep the grid and axis index_mappers the same as our index_mapper. if self._grid is not None: self._grid.mapper = self.index_mapper if self._axis is not None: self._axis.mapper = self.index_mapper self._either_mapper_changed() def _color_mapper_changed(self): self._either_mapper_changed() def _value_mapper_changed(self): self._color_mapper_changed() def _plot_changed(self): self.request_redraw() def _grid_visible_changed(self, old, new): self._grid.visible = new self.request_redraw() def _axis_visible_changed(self, old, new): self._axis.visible = new self.request_redraw() #------------------------------------------------------------------------ # Property setters and getters #------------------------------------------------------------------------ def _get_x_mapper(self): if self.orientation == "h": return self.index_mapper else: return None def _get_y_mapper(self): if self.orientation == "h": return None else: return self.index_mapper def _get_color_mapper(self): if self.plot: return self.plot.color_mapper elif self._color_mapper: return self._color_mapper else: return None def _set_color_mapper(self, val): self._color_mapper = val @cached_property def _get_value_mapper(self): return self._get_color_mapper() def _set_value_mapper(self, val): self._set_color_mapper(val) def _get_index(self): if self.plot and hasattr(self.plot, "color_data"): return self.plot.color_data elif self.plot and isinstance(self.plot, BaseXYPlot): return self.plot.index elif self._index: return self._index else: return None def _set_index(self, val): self._index = val # EOF chaco-4.5.0/chaco/color_mapper.py0000644000076600000240000003666012426452312017502 0ustar jrocherstaff00000000000000""" Defines the ColorMapper and ColorMapTemplate classes. """ # Major library imports from types import IntType, FloatType from numpy import arange, array, asarray, clip, divide, float32, int8, isinf, \ isnan, ones, searchsorted, sometrue, sort, take, uint8, where, zeros, \ linspace, ones_like # Enthought library imports from traits.api import Any, Array, Bool, Dict, Event, Float, HasTraits, \ Int, Property, Str, Trait # Relative imports from abstract_colormap import AbstractColormap from data_range_1d import DataRange1D from speedups import map_colors, map_colors_uint8 class ColorMapTemplate(HasTraits): """ A class representing the state of a ColorMapper, for use when persisting plots. """ # The segment data of the color map. segment_map = Any # The number of steps in the color map. steps = Int(256) # Low end of the color map range. range_low_setting = Trait('auto', 'auto', Float) # High end of the color map range. range_high_setting = Trait('auto', 'auto', Float) def __init__(self, colormap=None, **kwtraits): """ Creates this template from a color map instance or creates an empty template. """ if colormap: self.from_colormap(colormap) return def from_colormap(self, colormap): """ Populates this template from a color map. """ self.segment_map = colormap._segmentdata.copy() self.steps = colormap.steps self.range_low_setting = colormap.range.low_setting self.range_high_setting = colormap.range.high_setting return def to_colormap(self, range=None): """ Returns a ColorMapper instance from this template. """ colormap = ColorMapper(self.segment_map, steps = self.steps) if range: colormap.range = range else: colormap.range = DataRange1D(low = self.range_low_setting, high = self.range_high_setting) return colormap class ColorMapper(AbstractColormap): """ Represents a simple band-of-colors style of color map. The look-up transfer function is a simple linear function between defined intensities. There is no limit to the number of steps that can be defined. If the segment intervals contain very few array locations, quantization errors will occur. Construction of a ColorMapper can be done through the factory methods from_palette_array() and from_segment_map(). Do not make direct calls to the ColorMapper constructor. """ # The color table. color_bands = Property(Array) # The total number of color steps in the map. steps = Int(256) # The name of this color map. name = Str # Not used. low_pos = None # Not used. high_pos = None # A generic "update" event that generally means that anything that relies # on this mapper for visual output should do a redraw or repaint. updated = Event # Are the mapping arrays out of date? _dirty = Bool(True) # The raw segment data for creating the mapping array. _segmentdata = Dict # (Str, Tuple | List) #------------------------------------------------------------------------ # Static methods. #------------------------------------------------------------------------ @classmethod def from_palette_array(cls, palette, **traits): """ Creates a ColorMapper from a palette array. The palette colors are linearly interpolated across the range of mapped values. The *palette* parameter is a Nx3 or Nx4 array of intensity values, where N > 1:: [[R0, G0, B0], ... [R(N-1), G(N-1), B(N-1)]] [[R0, G0, B0, A0], ... [R(N-1), G(N-1), B(N-1), A(N-1]] """ palette = asarray(palette) n_colors, n_components = palette.shape if n_colors < 2: raise ValueError("Palette must contain at least two colors.") if n_components not in (3,4): raise ValueError("Palette must be of RGB or RGBA colors. " "Got %s color components." % n_components) # Compute the % offset for each of the color locations. offsets = linspace(0.0, 1.0, n_colors) # From the offsets and the color data, generate a segment map. segment_map = {} red_values = palette[:,0] segment_map['red'] = zip(offsets, red_values, red_values) green_values = palette[:,1] segment_map['green'] = zip(offsets, green_values, green_values) blue_values = palette[:,2] segment_map['blue'] = zip(offsets, blue_values, blue_values) if n_components == 3: alpha_values = ones(n_colors) else: alpha_values = palette[:,3] segment_map['alpha'] = zip(offsets, alpha_values, alpha_values) return cls(segment_map, **traits) @classmethod def from_segment_map(cls, segment_map, **traits): """ Creates a Colormapper from a segment map. The *segment_map* parameter is a dictionary with 'red', 'green', and 'blue' (and optionally 'alpha') entries. Each entry is a list of (x, y0, y1) tuples: * x: an offset in [0..1] (offsets within the list must be in ascending order) * y0: value for the color channel for values less than or equal to x * y1: value for the color channel for values greater than x When a data value gets mapped to a color, it will be normalized to be within [0..1]. For each RGB(A) component, the two adjacent values will be found in the segment_map. The mapped component value will be found by linearly interpolating the two values. Generally, y0==y1. Colormaps with sharp transitions will have y0!=y1 at the transitions. """ if 'alpha' not in segment_map: segment_map = segment_map.copy() segment_map['alpha'] = [(0.0, 1.0, 1.0), (1.0, 1.0, 1.0)] return cls(segment_map, **traits) @classmethod def from_file(cls, filename, **traits): """ Creates a ColorMapper from a file. The *filename* parameter is the name of a file whose lines each contain 4 or 5 float values between 0.0 and 1.0. The first value is an offset in the range [0..1], and the remaining 3 or 4 values are red, green, blue, and optionally alpha values for the color corresponding to that offset. The first line is assumed to contain the name of the colormap. """ colormap_file = open(filename, 'r') lines = colormap_file.readlines() colormap_file.close() rgba_arr = [[],[],[],[]] for line in lines[1:]: strvalues = line.strip().split() values = [float32(value) for value in strvalues] if len(values) > 4: channels = (0,1,2,3) else: channels = (0,1,2) for i in channels: channeltuple = (values[0], values[i+1], values[i+1]) rgba_arr[i].append(channeltuple) # Alpha is frequently unspecified. if len(rgba_arr[-1]) == 0: rgba_arr[-1] = [(0.0, 1.0, 1.0), (1.0, 1.0, 1.0)] if 'name' not in traits: # Don't override the code. traits['name'] = lines[0].strip() rgba_dict = { 'red': rgba_arr[0], 'green': rgba_arr[1], 'blue': rgba_arr[2], 'alpha': rgba_arr[3], } return cls(rgba_dict, **traits) #------------------------------------------------------------------------ # Public methods #------------------------------------------------------------------------ def __init__(self, segmentdata, **kwtraits): """ Creates a Colormapper from a segment map. The *segment_map* parameter is a dictionary with 'red', 'green', and 'blue' (and optionally 'alpha') entries. Each entry is a list of (x, y0, y1) tuples: * x: an offset in [0..1] (offsets within the list must be in ascending order) * y0: value for the color channel for values less than or equal to x * y1: value for the color channel for values greater than x When a data value gets mapped to a color, it will be normalized to be within [0..1]. For each RGB(A) component, the two adjacent values will be found in the segment_map. The mapped component value will be found by linearly interpolating the two values. Generally, y0==y1. Colormaps with sharp transitions will have y0!=y1 at the transitions. """ self._segmentdata = segmentdata super(ColorMapper, self).__init__(**kwtraits) return def map_screen(self, data_array): """ Maps an array of data values to an array of colors. """ if self._dirty: self._recalculate() rgba = map_colors(data_array, self.steps, self.range.low, self.range.high, self._red_lut, self._green_lut, self._blue_lut, self._alpha_lut) return rgba def map_index(self, ary): """ Maps an array of values to their corresponding color band index. """ if self._dirty: self._recalculate() indices = (ary - self.range.low) / (self.range.high - self.range.low) * self.steps return clip(indices.astype(IntType), 0, self.steps - 1) def reverse_colormap(self): """ Reverses the color bands of this colormap. """ for name in ("red", "green", "blue", "alpha"): data = asarray(self._segmentdata[name]) data[:, (1,2)] = data[:, (2,1)] data[:,0] = (1.0 - data[:,0]) self._segmentdata[name] = data[::-1] self._recalculate() def map_uint8(self, data_array): """ Maps an array of data values to an array of colors. """ if self._dirty: self._recalculate() rgba = map_colors_uint8(data_array, self.steps, self.range.low, self.range.high, self._red_lut_uint8, self._green_lut_uint8, self._blue_lut_uint8, self._alpha_lut_uint8) return rgba #------------------------------------------------------------------------ # Private methods #------------------------------------------------------------------------ def _get_color_bands(self): """ Gets the color bands array. """ if self._dirty: self._recalculate() luts = [self._red_lut, self._green_lut, self._blue_lut] if self.color_depth is 'rgba': luts.append(self._alpha_lut) result = zip(*luts) return result def _recalculate(self): """ Recalculates the mapping arrays. """ self._red_lut = self._make_mapping_array( self.steps, self._segmentdata['red'] ) self._green_lut = self._make_mapping_array( self.steps, self._segmentdata['green'] ) self._blue_lut = self._make_mapping_array( self.steps, self._segmentdata['blue'] ) self._alpha_lut = self._make_mapping_array( self.steps, self._segmentdata['alpha'] ) self._red_lut_uint8 = (self._red_lut * 255.0).astype('uint8') self._green_lut_uint8 = (self._green_lut * 255.0).astype('uint8') self._blue_lut_uint8 = (self._blue_lut * 255.0).astype('uint8') self._alpha_lut_uint8 = (self._alpha_lut * 255.0).astype('uint8') self.updated = True self._dirty = False return #### matplotlib #### def _make_mapping_array(self, n, data): """Creates an N-element 1-D lookup table The *data* parameter is a list of x,y0,y1 mapping correspondences (which can be lists or tuples), where all the items are values between 0 and 1, inclusive. The items in the mapping are: * x: a value being mapped * y0: the value of y for values of x less than or equal to the given x value. * y1: the value of y for values of x greater than the given x value. The two values of y allow for discontinuous mapping functions (for example, as might be found in a sawtooth function) The list must start with x=0, end with x=1, and all values of x must be in increasing order. Values between the given mapping points are determined by simple linear interpolation. The function returns an array "result" where result[x*(N-1)] gives the closest value for values of x between 0 and 1. """ try: adata = array(data) except: raise TypeError("data must be convertable to an array") shape = adata.shape if len(shape) != 2 and shape[1] != 3: raise ValueError("data must be nx3 format") x = adata[:,0] y0 = adata[:,1] y1 = adata[:,2] if x[0] != 0. or x[-1] != 1.0: raise ValueError( "data mapping points must start with x=0. and end with x=1") if sometrue(sort(x)-x): raise ValueError( "data mapping points must have x in increasing order") # begin generation of lookup table x = x * (n-1) lut = zeros((n,), float32) xind = arange(float32(n), dtype=float32) ind = searchsorted(x, xind)[1:-1] lut[1:-1] = ( divide(xind[1:-1] - take(x,ind-1), take(x,ind)-take(x,ind-1) ) *(take(y0,ind)-take(y1,ind-1)) + take(y1,ind-1)) lut[0] = y1[0] lut[-1] = y0[-1] # ensure that the lut is confined to values between 0 and 1 by clipping it lut = lut.clip(0, 1) return lut #### matplotlib #### def _map(self, X): """ Maps from a scalar or an array to an RGBA value or array. The *X* parameter is either a scalar or an array (of any dimension). If it is scalar, the function returns a tuple of RGBA values; otherwise it returns an array with the new shape = oldshape+(4,). Any values that are outside the 0,1 interval are clipped to that interval before generating RGB values. This is no longer used in this class. It has been deprecated and retained for API compatibility. """ if type(X) in [IntType, FloatType]: vtype = 'scalar' xa = array([X]) else: vtype = 'array' xa = asarray(X) # assume the data is properly normalized #xa = where(xa>1.,1.,xa) #xa = where(xa<0.,0.,xa) nanmask = isnan(xa) xa = where(nanmask, 0, (xa * (self.steps-1)).astype(int)) rgba = zeros(xa.shape+(4,), float) rgba[...,0] = where(nanmask, 0, take(self._red_lut, xa)) rgba[...,1] = where(nanmask, 0, take(self._green_lut, xa)) rgba[...,2] = where(nanmask, 0, take(self._blue_lut, xa)) rgba[...,3] = where(nanmask, 0, take(self._alpha_lut, xa)) if vtype == 'scalar': rgba = tuple(rgba[0,:]) return rgba def _range_changed(self, old, new): if old is not None: old.on_trait_change(self._range_change_handler, "updated", remove = True) if new is not None: new.on_trait_change(self._range_change_handler, "updated") self.updated = new def _range_change_handler(self, obj, name, new): "Handles the range changing; dynamically attached to our ranges" self.updated = obj # EOF chaco-4.5.0/chaco/color_spaces.py0000644000076600000240000002464612426462254017503 0ustar jrocherstaff00000000000000""" Conversion functions between various color spaces. The implementations and data are mostly taken from the old scipy.sandbox.image package. The CIE XYZ tristimulus colorspace with a standard D65 whitepoint is the default interchange color space for the implementations here. This is a useful whitepoint for viewing on computer monitors. However, it should be noted that the dimmer D50 whitepoint is often used in print applications. Notably, ICC profiles use the XYZ space with a D50 whitepoint as one of its standard interchange color spaces. """ import numpy as np from numpy.linalg import inv, solve #### Utilities ################################################################ def convert(matrix, TTT, axis=-1): """ Apply linear matrix transformation to an array of color triples. Parameters ---------- matrix : float array (3, 3) The transformation to apply. TTT : float array The set of colors to transform. axis : int, optional The axis of `TTT` along which the color triples extend. Returns ------- OUT : float array The transformed colors. """ TTT = np.asarray(TTT) if (axis != 0): TTT = np.swapaxes(TTT, 0, axis) oldshape = TTT.shape TTT = np.reshape(TTT, (3, -1)) OUT = np.dot(matrix, TTT) OUT.shape = oldshape if (axis != 0): OUT = np.swapaxes(OUT, axis, 0) return OUT def makeslices(n): """ Return a list of `n` slice objects. Each slice object corresponds to [:] without arguments. """ slices = [slice(None)] * n return slices def separate_colors(xyz, axis=-1): """ Separate an array of color triples into three arrays, one for each color axis. Parameters ---------- xyz : float array axis : int, optional The axis along which the color triples extend. Returns ------- x : float array y : float array z : float array The separate color arrays. axis : int The axis along which they need to be reassembled. """ n = len(xyz.shape) if axis < 0: axis = n + axis slices = makeslices(n) slices[axis] = 0 x = xyz[slices] slices[axis] = 1 y = xyz[slices] slices[axis] = 2 z = xyz[slices] return x, y, z, axis def join_colors(c1, c2, c3, axis): """ Rejoin the separated colors into a single array. """ c1 = np.asarray(c1) c2 = np.asarray(c2) c3 = np.asarray(c3) newshape = c1.shape[:axis] + (1,) + c1.shape[axis:] c1.shape = c2.shape = c3.shape = newshape return np.concatenate((c1, c2, c3), axis=axis) def triwhite(x, y): """ Convert x,y chromaticity coordinates to XYZ tristimulus values. """ X = x / y Y = 1.0 Z = (1-x-y)/y return [X, Y, Z] #### Data ##################################################################### # From the sRGB specification. xyz_from_rgb = np.array([[0.412453, 0.357580, 0.180423], [0.212671, 0.715160, 0.072169], [0.019334, 0.119193, 0.950227]]) rgb_from_xyz = inv(xyz_from_rgb) # XYZ white-point coordinates # from http://en.wikipedia.org/wiki/Standard_illuminant whitepoints = { 'CIE A': ['Normal incandescent', triwhite(0.44757, 0.40745)], 'CIE B': ['Direct sunlight', triwhite(0.34842, 0.35161)], 'CIE C': ['Average sunlight', triwhite(0.31006, 0.31616)], 'CIE E': ['Normalized reference', triwhite(1.0/3, 1.0/3)], 'D50': ['Bright tungsten', triwhite(0.34567, 0.35850)], 'D55': ['Cloudy daylight', triwhite(0.33242, 0.34743)], 'D65': ['Daylight', triwhite(0.31271, 0.32902)], 'D75': ['?', triwhite(0.29902, 0.31485)], } #### Conversion routines ###################################################### def xyz2lab(xyz, axis=-1, wp=whitepoints['D65'][-1]): """ Convert XYZ tristimulus values to CIE L*a*b*. Parameters ---------- xyz : float array XYZ values. axis : int, optional The axis of the XYZ values. wp : list of 3 floats, optional The XYZ tristimulus values of the whitepoint. Returns ------- lab : float array The L*a*b* colors. """ x, y, z, axis = separate_colors(xyz, axis) xn, yn, zn = x/wp[0], y/wp[1], z/wp[2] def f(t): eps = 216/24389. kap = 24389/27. return np.where(t > eps, np.power(t, 1.0/3), (kap*t + 16.0)/116) fx, fy, fz = f(xn), f(yn), f(zn) L = 116*fy - 16 a = 500*(fx - fy) b = 200*(fy - fz) return join_colors(L, a, b, axis) def lab2xyz(lab, axis=-1, wp=whitepoints['D65'][-1]): """ Convert CIE L*a*b* colors to XYZ tristimulus values. Parameters ---------- lab : float array L*a*b* values. axis : int, optional The axis of the XYZ values. wp : list of 3 floats, optional The XYZ tristimulus values of the whitepoint. Returns ------- xyz : float array The XYZ colors. """ lab = np.asarray(lab) L, a, b, axis = separate_colors(lab, axis) fy = (L+16)/116.0 fz = fy - b / 200. fx = a/500.0 + fy def finv(y): eps3 = (216/24389.)**3 kap = 24389/27. return np.where(y > eps3, np.power(y, 3), (116*y - 16)/kap) xr, yr, zr = finv(fx), finv(fy), finv(fz) return join_colors(xr*wp[0], yr*wp[1], zr*wp[2], axis) # RGB values that will be displayed on a screen are always nonlinear # R'G'B' values. To get the XYZ value of the color that will be # displayed you need a calibrated monitor with a profile. # But, for quick-and-dirty calculation you can often assume the standard # sR'G'B' coordinate system for your computer, and so the rgbp2rgb will # put you in the linear coordinate system (assuming normalized to [0,1] # sR'G'B' coordinates) # # sRGB <-> sR'G'B' equations from # http://www.w3.org/Graphics/Color/sRGB # http://www.srgb.com/basicsofsrgb.htm # Macintosh displays are usually gamma = 1.8 def rgb2rgbp(rgb, gamma=None): """ Convert linear RGB coordinates to nonlinear R'G'B' coordinates. Parameters ---------- rgb : float array gamma : float, optional If provided, then this value of gamma will be used to correct the colors. If not provided, then the standard sR'G'B' space will be assumed. It is almost, but not quite equivalent to a gamma of 2.2. Returns ------- rgbp : float array """ rgb = np.asarray(rgb) if gamma is None: eps = 0.0031308 mask = rgb < eps rgbp = np.empty_like(rgb) rgbp[mask] = 12.92 * rgb[mask] rgbp[~mask] = 1.055*rgb[~mask]**(1.0/2.4) - 0.055 return rgbp else: return rgb**(1.0/gamma) def rgbp2rgb(rgbp, gamma=None): """ Convert nonlinear R'G'B' coordinates to linear RGB coordinates. Parameters ---------- rgbp : float array gamma : float, optional If provided, then this value of gamma will be used to correct the colors. If not provided, then the standard sR'G'B' space will be assumed. It is almost, but not quite equivalent to a gamma of 2.2. Returns ------- rgb : float array """ rgbp = np.asarray(rgbp) if gamma is None: eps = 0.04045 mask = rgbp <= eps rgb = np.empty_like(rgbp) rgb[mask] = rgbp[mask] / 12.92 rgb[~mask] = ((rgbp[~mask] + 0.055) / 1.055) ** 2.4 return rgb else: return rgbp**gamma def xyz2rgb(xyz, axis=-1): """ Convert XYZ tristimulus values to linear RGB coordinates. Parameters ---------- xyz : float array XYZ values. axis : int, optional The axis of the XYZ values. Returns ------- rgb : float array The RGB colors. """ return convert(rgb_from_xyz, xyz, axis) def rgb2xyz(rgb, axis=-1): """ Convert linear RGB coordinates to XYZ tristimulus values. Parameters ---------- rgb : float array RGB values. axis : int, optional The axis of the XYZ values. Returns ------- xyz : float array The XYZ colors. """ return convert(xyz_from_rgb, rgb, axis) def srgb2xyz(srgb, axis=-1): """ Convert sR'G'B' colors to XYZ. Parameters ---------- srgb : float array sR'G'B' values. axis : int, optional The axis of the XYZ values. Returns ------- xyz : float array The XYZ colors. """ return rgb2xyz(rgbp2rgb(srgb), axis=axis) def xyz2srgb(xyz, axis=-1): """ Convert XYZ colors to sR'G'B'. Parameters ---------- xyz : float array XYZ values. axis : int, optional The axis of the XYZ values. Returns ------- srgb : float array The sR'G'B' colors. """ return rgb2rgbp(xyz2rgb(xyz, axis=axis)) def xyz2xyz(xyz): """ Identity mapping. """ return xyz def xyz2msh(xyz, axis=-1, wp=whitepoints['D65'][-1]): """ Convert XYZ tristimulus values to Msh. Msh is a hemispherical coordinate system derived from L*a*b*. The origin remains the same. M is the distance from the origin. s is an inclination angle from the vertical corresponding to saturation. h is the azimuthal angle corresponding to hue. Moreland, Kenneth. Diverging Color Maps for Scientific Visualization (Expanded). http://www.sandia.gov/~kmorel/documents/ColorMaps/ColorMapsExpanded.pdf Parameters ---------- xyz : float array XYZ values. axis : int, optional The axis of the XYZ values. wp : list of 3 floats, optional The XYZ tristimulus values of the whitepoint. Returns ------- msh : float array The Msh colors. """ L, a, b, axis = separate_colors(xyz2lab(xyz, axis=axis, wp=wp), axis) M = np.sqrt(L*L + a*a + b*b) s = np.arccos(L / M) h = np.arctan2(b, a) return join_colors(M, s, h, axis) def msh2xyz(msh, axis=-1, wp=whitepoints['D65'][-1]): """ Convert Msh values to XYZ tristimulus values. Parameters ---------- msh : float array The Msh colors. axis : int, optional The axis of the XYZ values. wp : list of 3 floats, optional The XYZ tristimulus values of the whitepoint. Returns ------- xyz : float array XYZ values. """ M, s, h, axis = separate_colors(msh, axis) L = M * np.cos(s) a = M * np.sin(s) * np.cos(h) b = M * np.sin(s) * np.sin(h) return lab2xyz(join_colors(L, a, b, axis), axis=axis, wp=wp) chaco-4.5.0/chaco/colormap_generators.py0000644000076600000240000001037212426462254021063 0ustar jrocherstaff00000000000000""" Generate parameteric colormaps. Diverging colormaps can be generated via Kenneth Moreland's procedure using ``generate_diverging_palette()``. Moreland, Kenneth. Diverging Color Maps for Scientific Visualization (Expanded). http://www.sandia.gov/~kmorel/documents/ColorMaps/ColorMapsExpanded.pdf Dave Green's cubehelix family of colormaps can be generated using ``generate_cubehelix_palette()``. Green, D. A., 2011, A colour scheme for the display of astronomical intensity images. Bulletin of the Astronomical Society of India, 39, 289. (2011BASI...39..289G at ADS.) http://adsabs.harvard.edu/abs/2011arXiv1108.5083G https://www.mrao.cam.ac.uk/~dag/CUBEHELIX/ """ import numpy as np from .color_spaces import msh2xyz, srgb2xyz, xyz2msh, xyz2srgb def adjust_hue(msh_sat, m_unsat): """ Adjust the hue when interpolating to an unsaturated color. Parameters ---------- msh_sat : float array (3,) Saturated Msh color at an endpoint of the colormap. m_unsat : float The magnitude of the target unsaturated color. Returns ------- h_adjusted : float The adjusted target hue value. """ m_sat, s_sat, h_sat = msh_sat if m_sat >= m_unsat: return h_sat else: spin = s_sat * np.sqrt(m_unsat*m_unsat - m_sat*m_sat) / (m_sat * np.sin(s_sat)) if h_sat > -np.pi / 3: return h_sat + spin else: return h_sat - spin def generate_diverging_palette(srgb1, srgb2, n_colors=256): """ Generate a diverging color palette with two endpoint colors. Parameters ---------- srgb1, srgb2 : float array (3,) RGB colors for the endpoints. An unsaturated white/grey color will be in the middle. n_colors : int, optional The number of colors to generate in the palette. Returns ------- srgb_palette : float array (n_colors, 3) RGB color palette. """ m1, s1, h1 = np.squeeze(xyz2msh(srgb2xyz([srgb1]))) m2, s2, h2 = np.squeeze(xyz2msh(srgb2xyz([srgb2]))) mmid = max(m1, m2, 88.0) hmid1 = 0.0 hmid2 = 0.0 x = np.linspace(0.0, 1.0, n_colors) # srgb1 -> white half1 = 2 * x[x <= 0.5] # white -> srgb2 half2 = 2 * x[x > 0.5] - 1.0 if s1 > 0.05: hmid1 = adjust_hue((m1, s1, h1), mmid) if s2 > 0.05: hmid2 = adjust_hue((m2, s2, h2), mmid) m_palette = np.hstack([ half1 * mmid + (1 - half1) * m1, half2 * m2 + (1 - half2) * mmid, ]) s_palette = np.hstack([ (1 - half1) * s1, half2 * s2, ]) h_palette = np.hstack([ half1 * hmid1 + (1 - half1) * h1, half2 * h2 + (1 - half2) * hmid2, ]) msh_palette = np.column_stack([m_palette, s_palette, h_palette]) srgb_palette = xyz2srgb(msh2xyz(msh_palette)).clip(0.0, 1.0) return srgb_palette def generate_cubehelix_palette(start=0.5, rot=-1.5, saturation=1.2, lightness_range=(0.0, 1.0), gamma=1.0, n_colors=256): """ Generate a sequential color palette from black to white spiraling through intermediate colors. Parameters ---------- start : float between 0.0 and 3.0, optional The starting hue. 0 is blue, 1 is red, 2 is green. rot : float, optional How many rotations to go in hue space. saturation : float, optional The saturation intensity factor. lightness_range : (float, float), optional The range of lightness values to interpolate between. gamma : float, optional The gamma exponent adjustment to apply to the lightness values. n_colors : int, optional The number of colors to generate in the palette. Returns ------- srgb_palette : float array (n_colors, 3) RGB color palette. """ x = np.linspace(lightness_range[0], lightness_range[1], n_colors) theta = 2.0 * np.pi * (start / 3.0 + rot * x + 1.) x **= gamma amplitude = saturation * x * (1 - x) / 2.0 red = x + amplitude * (-0.14861*np.cos(theta) + 1.78277*np.sin(theta)) green = x + amplitude * (-0.29227*np.cos(theta) - 0.90649*np.sin(theta)) blue = x + amplitude * (1.97294*np.cos(theta)) srgb_palette = np.column_stack([red, green, blue]).clip(0.0, 1.0) return srgb_palette chaco-4.5.0/chaco/colormapped_scatterplot.py0000644000076600000240000004065312426462425021754 0ustar jrocherstaff00000000000000""" Defines the ColormappedScatterPlot and ColormappedScatterPlotView classes. """ from __future__ import with_statement # Major library imports from numpy import argsort, array, concatenate, nonzero, invert, take, \ isnan, transpose, newaxis, zeros, ndarray # Enthought library imports from kiva.constants import STROKE from traits.api import Dict, Enum, Float, Instance, on_trait_change from traitsui.api import Item, RangeEditor # Local, relative imports from array_data_source import ArrayDataSource from base import left_shift, right_shift from abstract_colormap import AbstractColormap from scatterplot import ScatterPlot, ScatterPlotView class ColormappedScatterPlotView(ScatterPlotView): """ Traits UI View for customizing a color-mapped scatter plot. """ def __init__(self): super(ColormappedScatterPlotView, self).__init__() vgroup = self.content vgroup.content[0].content.append(Item("fill_alpha", label="Fill alpha", editor=RangeEditor(low=0.0, high=1.0))) return class ColormappedScatterPlot(ScatterPlot): """ A scatter plot that allows each point to take on a different color, corresponding to a color map. If the **color_data** or **color_mapper** attributes are None, then it behaves like a normal ScatterPlot. """ # Source for color data. color_data = Instance(ArrayDataSource) # Mapping for colors. color_mapper = Instance(AbstractColormap) # The alpha value to apply to the result of the color-mapping process. # (This makes it easier to create color maps without having to worry # about alpha.) fill_alpha = Float(1.0) # Determines what drawing approach to use: # # banded: # Draw the points color-band by color-band, thus reducing the number of # set_stroke_color() calls. Disadvantage is that some colors will # appear more prominently than others if there are a lot of # overlapping points. # bruteforce: # Set the stroke color before drawing each marker. Slower, but doesn't # produce the banding effect that puts some colors on top of others; # useful if there is a lot of overlap of the data. # auto: # Determines which render method to use based on the number of points # # TODO: Based on preliminary results, "banded" isn't significantly # more expensive than "bruteforce" for small datasets (<1000), # so perhaps banded should be removed. render_method = Enum("auto", "banded", "bruteforce") # A dict mapping color-map indices to arrays of indices into self.data. # This is used for the "banded" render method. # This mapping is only valid if **_cache_valid** is True. _index_bands = Dict() # Traits UI View for customizing the plot. Overrides the ScatterPlot value. traits_view = ColormappedScatterPlotView() #------------------------------------------------------------------------ # BaseXYPlot interface #------------------------------------------------------------------------ def map_screen(self, data_array): """ Maps an array of data points into screen space, and returns them as an array. The *data_array* parameter must be an Nx2 (index, value) or Nx3 (index, value, color_value) array. The returned array is an Nx2 array of (x, y) tuples. """ if len(data_array)>0: if data_array.shape[1] == 3: data_array = data_array[:, :2] return super(ColormappedScatterPlot, self).map_screen(data_array) def _draw_plot(self, gc, view_bounds=None, mode="normal"): """ Draws the 'plot' layer. Overrides BaseXYPlot, which isn't really fully generic (it assumes that the output of map_screen() is sufficient to render the data). """ self._gather_points() if len(self._cached_data_pts) == 0: pass elif self._cached_data_pts.shape[1] == 2: # Take into account fill_alpha even if we are rendering with only two values old_color = self.color self.color = tuple(self.fill_alpha * array(self.color_)) super(ColormappedScatterPlot, self)._draw_component(gc, view_bounds, mode) self.color = old_color else: colors = self._cached_data_pts[:,2] screen_pts = self.map_screen(self._cached_data_pts) pts = concatenate((screen_pts, colors[:, newaxis]), axis=1) self._render(gc, pts) return def _gather_points(self): """ Collects the data points that are within the plot bounds and caches them """ if self._cache_valid: return if not self.index or not self.value: self._cached_data_pts = [] self._cache_valid = True return index, index_mask = self.index.get_data_mask() value, value_mask = self.value.get_data_mask() if len(index) == 0 or len(value) == 0 or len(index) != len(value): self._cached_data_pts = [] self._cache_valid = True return index_range_mask = self.index_mapper.range.mask_data(index) value_range_mask = self.value_mapper.range.mask_data(value) nan_mask = invert(isnan(index_mask)) & invert(isnan(value_mask)) point_mask = index_mask & value_mask & nan_mask & \ index_range_mask & value_range_mask if self.color_data is not None: if self.color_data.is_masked(): color_data, color_mask = self.color_data.get_data_mask() point_mask = point_mask & color_mask else: color_data = self.color_data.get_data() #color_nan_mask = isreal(color_data) color_nan_mask = invert(isnan(color_data)) point_mask = point_mask & color_nan_mask points = transpose(array((index, value, color_data))) else: points = transpose(array((index, value))) self._cached_data_pts = points[point_mask] self._cached_point_mask = point_mask self._cache_valid = True return def _render(self, gc, points): """ Actually draws the plot. Overrides the ScatterPlot implementation. """ # If we don't have a color data set, then use the base class to render if (self.color_mapper is None) or (self.color_data is None): return super(ColormappedScatterPlot, self)._render(gc, points) # If the GC doesn't have draw_*_at_points, then use bruteforce if hasattr(gc, 'draw_marker_at_points') or hasattr(gc, 'draw_path_at_points'): batch_capable = True else: batch_capable = False if self.render_method == 'auto': method = self._calc_render_method(len(points)) else: method = self.render_method with gc: if method == 'bruteforce' or (not batch_capable): self._render_bruteforce(gc, points) elif method == 'banded': self._render_banded(gc, points) return #------------------------------------------------------------------------ # Private methods #------------------------------------------------------------------------ def _compute_bands(self, points, smartmode=False): """ Sorts self.data into a list of arrays of data points by color, filling in self._index_bands. If *smartmode* is True, then it first calls _calc_render_method() to see which rendering method is optimal for the number of points and the distribution of color indices; if the rendering method is 'bruteforce', then this method short-circuits and returns without doing anything. """ if len(points) == 0: return if self.color_mapper is None: return # map the V values in the (x,y,v) self.data array color_data = points[:,2] color_indices = self.color_mapper.map_index(color_data) if smartmode and self.render_method == 'bruteforce': pass else: # shuffle_indices indicates how to sort the points in self.data # so that their color_indices are in order. We don't really care # about the sorting so much as the fact that once they are sorted, # points of the same color are grouped together into "bands". shuffle_indices = argsort(color_indices) # This pulls values from the color_indices array into # sorted_color_indices, using the results of the sort we just did. sorted_color_indices = take(color_indices, shuffle_indices) # Now we want to determine where the continuous bands are. We do # this by right-shifting the sorted_color_indices array, subtracting # it from the original, and looking for all the nonzero points. shifted = right_shift(sorted_color_indices, sorted_color_indices[0]) start_indices = concatenate([[0], nonzero(sorted_color_indices - shifted)[0]]) end_indices = left_shift(start_indices, len(sorted_color_indices)) # Store the shuffled indices in self._index_bands. We don't store the # actual data points because we need to allow the renderer to index into # the mapped XY screen positions. self._index_bands = {} for (start, end) in zip(start_indices, end_indices): color_index = sorted_color_indices[start] self._index_bands[color_index] = shuffle_indices[start:end] self._color_indices = color_indices self._cache_valid = True return def _calc_render_method(self, numpoints): """ Returns a string indicating the render method. """ if numpoints > 1000 and isinstance(self.marker_size, float): return 'banded' else: return "bruteforce" def _set_draw_info(self, gc, mode, color, outline_color=None, outline_weight=None): """ Sets the stroke color, fill color, and line width on the graphics context. """ color = tuple(color[:3]) + (self.fill_alpha,) if mode == STROKE: if outline_color is not None: gc.set_stroke_color( color ) else: if outline_color is not None: gc.set_stroke_color( outline_color ) gc.set_fill_color( color ) if outline_weight is not None: gc.set_line_width(outline_weight) return def _render_banded(self, gc, points): """ Draws the points color-band by color-band. """ self._compute_bands(points) # Grab the XY values corresponding to each color band of points xy_points = points[:,0:2] marker = self.marker_ size = self.marker_size assert isinstance(size, float), "Variable size markers not implemented for banded rendering" # Set up the GC for drawing gc.set_line_dash( None ) if marker.draw_mode == STROKE: gc.set_line_width(self.line_width) gc.begin_path() cmap = self.color_mapper if (hasattr(gc, 'draw_marker_at_points') and self.marker not in ('custom', 'circle', 'diamond')): # This is the fastest method: we use one of the built-in markers. color_bands = cmap.color_bands # Initial setup of drawing parameters self._set_draw_info(gc, marker.draw_mode, color_bands[0], self.outline_color_, self.line_width) index_bands = self._index_bands mode = marker.draw_mode for color_index in index_bands.keys(): self._set_draw_info(gc, mode, color_bands[color_index]) gc.draw_marker_at_points(xy_points[index_bands[color_index]], size, marker.kiva_marker) elif hasattr( gc, 'draw_path_at_points' ): point_bands = {} for color_index, indices in self._index_bands.items(): point_bands[color_index] = xy_points[indices] # We have to construct the path for the marker. if self.marker != 'custom': path = gc.get_empty_path() # turn the class into an instance... we should make add_to_path a # class method at some point. marker().add_to_path(path, size) mode = marker.draw_mode else: path = self.custom_symbol mode = STROKE color_bands = cmap.color_bands for color_index, xy in point_bands.items(): self._set_draw_info(gc, mode, color_bands[color_index], self.outline_color_, self.line_width) gc.draw_path_at_points(xy, path, mode) else: raise RuntimeError, "Batch drawing requested on non-batch-capable GC." return def _render_bruteforce(self, gc, points): """ Draws the points, setting the stroke color for each one. """ x, y, colors = transpose(points) # Map the colors colors = self.color_mapper.map_screen(colors) alphas = (zeros(len(colors))+self.fill_alpha)[:, newaxis] colors = concatenate((colors[:, :3], alphas), axis=1) with gc: gc.clip_to_rect(self.x, self.y, self.width, self.height) gc.set_stroke_color(self.outline_color_) gc.set_line_width(self.line_width) marker_cls = self.marker_ marker_size = self.marker_size if isinstance(marker_size, ndarray) and self._cached_point_mask is not None: marker_size = marker_size[self._cached_point_mask] mode = marker_cls.draw_mode if marker_cls != "custom": if (hasattr(gc, "draw_marker_at_points") and self.marker not in ('custom', 'circle', 'diamond')): draw_func = lambda x, y, size: gc.draw_marker_at_points([[x,y]], size, marker_cls.kiva_marker) elif hasattr(gc, "draw_path_at_points"): # turn the class into an instance... we should make add_to_path a # class method at some point. m = marker_cls() def draw_func(x, y, size): path = gc.get_empty_path() m.add_to_path(path, size) gc.draw_path_at_points([[x, y]], path, mode) else: m = marker_cls() def draw_func(x, y, size): gc.translate_ctm(x, y) gc.begin_path() m.add_to_path(gc, size) gc.draw_path(mode) gc.translate_ctm(-x, -y) for i in range(len(x)): if isinstance(marker_size, float): size = marker_size else: size = marker_size[i] gc.set_fill_color(colors[i]) draw_func(x[i], y[i], size) else: path = marker_cls.custom_symbol for i in range(len(x)): gc.set_fill_color(colors[i]) gc.draw_path_at_points([[x[i], y[i]]], path, STROKE) #------------------------------------------------------------------------ # Event handlers #------------------------------------------------------------------------ def _color_data_changed(self, old, new): if old is not None: old.on_trait_change(self._either_data_changed, "data_changed", remove=True) if new is not None: new.on_trait_change(self._either_data_changed, "data_changed") self._either_data_changed() return def _color_mapper_changed(self, old, new): self._cache_valid = False if hasattr(new, 'range') and new.range is None and old is not None: # Someone passed in a ColorMapper that has no range associated with # it. Use the range on the old ColorMapper. new.range = old.range self.invalidate_draw() self.request_redraw() return @on_trait_change('color_mapper:updated') def _color_mapper_updated(self): self.invalidate_draw() self.request_redraw() def _fill_alpha_changed(self): self.invalidate_draw() self.request_redraw() return # EOF chaco-4.5.0/chaco/colormapped_selection_overlay.py0000644000076600000240000001363312426452312023126 0ustar jrocherstaff00000000000000""" Defines the ColormappedSelectionOverlay class. """ from numpy import logical_and # Enthought library imports from traits.api import Any, Bool, Float, Instance, Property, Enum # Local imports from abstract_overlay import AbstractOverlay from colormapped_scatterplot import ColormappedScatterPlot class ColormappedSelectionOverlay(AbstractOverlay): """ Overlays and changes a ColormappedScatterPlot to fade its non-selected points to a very low alpha. """ # The ColormappedScatterPlot that this overlay is listening to. # By default, it looks at self.component plot = Property # The amount to fade the unselected points. fade_alpha = Float(0.15) # The minimum difference, in float percent, between the starting and ending # selection values, if range selection mode is enabled minimum_delta = Float(0.01) # Outline width for selected points. selected_outline_width = Float(1.0) # Outline width for unselected points. unselected_outline_width = Float(0.0) # The type of selection used by the data source. selection_type = Enum('range', 'mask') _plot = Instance(ColormappedScatterPlot) _visible = Bool(False) _old_alpha = Float _old_outline_color = Any _old_line_width = Float(0.0) def __init__(self, component=None, **kw): super(ColormappedSelectionOverlay, self).__init__(**kw) self.component = component return def overlay(self, component, gc, view_bounds=None, mode="normal"): """ Draws this component overlaid on another component. Implements AbstractOverlay. """ if not self._visible: return plot = self.plot datasource = plot.color_data if self.selection_type == 'range': selections = datasource.metadata["selections"] if selections is not None and len(selections) == 0: return low, high = selections if abs(high - low) / abs(high + low) < self.minimum_delta: return # Mask the data with just the points falling within the data # range selected on the colorbar data_pts = datasource.get_data() mask = (data_pts >= low) & (data_pts <= high) elif self.selection_type == 'mask': mask = reduce(logical_and, datasource.metadata["selection_masks"]) if sum(mask)<2: return datasource.set_mask(mask) # Store the current plot color settings before overwriting them fade_outline_color = plot.outline_color_ # Overwrite marker outline color and fill alpha settings of # the plot, then manually invoke the plot to draw onto the GC. plot.outline_color = list(self._old_outline_color[:3]) + [1.0] plot.fill_alpha = 1.0 plot.line_width = self.selected_outline_width plot._draw_plot(gc, view_bounds, mode) # Restore the plot's previous color settings and data mask. plot.fill_alpha = self.fade_alpha plot.outline_color = fade_outline_color plot.line_width = self.unselected_outline_width datasource.remove_mask() return def _component_changed(self, old, new): if old: old.on_trait_change(self.datasource_change_handler, "color_data", remove=True) if new: new.on_trait_change(self.datasource_change_handler, "color_data") self._old_alpha = new.fill_alpha self._old_outline_color = new.outline_color self._old_line_width = new.line_width self.datasource_change_handler(new, "color_data", None, new.color_data) return def datasource_change_handler(self, obj, name, old, new): if old: old.on_trait_change(self.selection_change_handler, "metadata_changed", remove=True) if new: new.on_trait_change(self.selection_change_handler, "metadata_changed") self.selection_change_handler(new, "metadata_changed", None, new.metadata) return def selection_change_handler(self, obj, name, old, new): if self.selection_type == 'range': selection_key = 'selections' elif self.selection_type == 'mask': selection_key = 'selection_masks' if type(new) == dict and new.get(selection_key, None) is not None \ and len(new[selection_key]) > 0: if not self._visible: # We have a new selection, so replace the colors on the plot with the # faded alpha and colors plot = self.plot # Save the line width and set it to zero for the unselected points self._old_line_width = plot.line_width plot.line_width = self.unselected_outline_width # Save the outline color and set it to the faded version self._old_outline_color = plot.outline_color_ outline_color = list(plot.outline_color_) if len(outline_color) == 3: outline_color += [self.fade_alpha] else: outline_color[3] = self.fade_alpha plot.outline_color = outline_color # Save the alpha value and set it to a faded version self._old_alpha = plot.fill_alpha plot.fill_alpha = self.fade_alpha self.plot.invalidate_draw() self._visible = True else: self.plot.fill_alpha = self._old_alpha self.plot.outline_color = self._old_outline_color self.plot.line_width = self._old_line_width self.plot.invalidate_draw() self._visible = False self.plot.request_redraw() return def _get_plot(self): if self._plot is not None: return self._plot else: return self.component def _set_plot(self, val): self._plot = val # EOF chaco-4.5.0/chaco/contour/0000755000076600000240000000000012426466422016133 5ustar jrocherstaff00000000000000chaco-4.5.0/chaco/contour/__init__.py0000644000076600000240000000000012426452312020223 0ustar jrocherstaff00000000000000chaco-4.5.0/chaco/contour/cntr.c0000644000076600000240000015725312426452312017253 0ustar jrocherstaff00000000000000/* cntr.c General purpose contour tracer for quadrilateral meshes. Handles single level contours, or region between a pair of levels. The routines that do all the work, as well as the explanatory comments, came from gcntr.c, part of the GIST package. The original mpl interface was also based on GIST. The present interface uses parts of the original, but places them in the entirely different framework of a Python type. It was written by following the Python "Extending and Embedding" tutorial. $Id: cntr.c,v 1.3 2005/06/02 22:02:32 jdh2358 Exp $ */ #include #include "structmember.h" #include #include #ifdef NUMPY #include "numpy/arrayobject.h" # ifndef PyArray_SBYTE # include "numpy/oldnumeric.h" # include "numpy/old_defines.h" # endif #else # include "Numeric/arrayobject.h" # define PyArray_UBYTELTR 'b' #endif /* Note that all arrays in these routines are Fortran-style, in the sense that the "i" index varies fastest; the dimensions of the corresponding C array are z[jmax][imax] in the notation used here. We can identify i and j with the x and y dimensions, respectively. */ /* What is a contour? * * Given a quadrilateral mesh (x,y), and values of a z at the points * of that mesh, we seek a set of polylines connecting points at a * particular value of z. Each point on such a contour curve lies * on an edge of the mesh, at a point linearly interpolated to the * contour level z0 between the given values of z at the endpoints * of the edge. * * Identifying these points is easy. Figuring out how to connect them * into a curve -- or possibly a set of disjoint curves -- is difficult. * Each disjoint curve may be either a closed circuit, or it may begin * and end on a mesh boundary. * * One of the problems with a quadrilateral mesh is that when the z * values at one pair of diagonally opposite points lie below z0, and * the values at the other diagonal pair of the same zone lie above z0, * all four edges of the zone are cut, and there is an ambiguity in * how we should connect the points. I call this a saddle zone. * The problem is that two disjoint curves cut through a saddle zone * (I reject the alternative of connecting the opposite points to make * a single self-intersecting curve, since those make ugly contour plots * -- I've tried it). The real problem with saddle zones is that you * need to communicate the connectivity decision you make back to the * calling routine, since for the next contour level, we need to tell * the contour tracer to make the same decision as on the previous * level. The input/output triangulation array is the solution to this * nasty problem. * * Another complicating factor is that there may be logical holes in * the mesh -- zones which do not exist. We want our contours to stop * if they hit the edge of such a zone, just as if they'd hit the edge * of the whole mesh. The input region array addresses this issue. * * Yet another complication: We may want a list of closed polygons which * outline the region between two contour levels z0 and z1. These may * include sections of the mesh boundary (including edges of logical * holes defined by the region array), in addition to sections of the * contour curves at one or both levels. This introduces a huge * topological problem -- if one of the closed contours (possibly * including an interior logical hole in the mesh, but not any part of * the boundary of the whole mesh) encloses a region which is not * between z0 and z1, that curve must be connected by a slit (or "branch * cut") to the enclosing curve, so that the list of disjoint polygons * we return is each simply connected. * * Okay, one final stunning difficulty: For the two level case, no * individual polygon should have more than a few thousand sides, since * huge filled polygons place an inordinate load on rendering software, * which needs an amount of scratch space proportional to the number * of sides it needs to fill. So in the two level case, we want to * chunk the mesh into rectangular pieces of no more than, say, 30x30 * zones, which keeps each returned polygon to less than a few thousand * sides (the worst case is very very bad -- you can easily write down * a function and two level values which produce a polygon that cuts * every edge of the mesh twice). */ /* * Here is the numbering scheme for points, edges, and zones in * the mesh -- note that each ij corresponds to one point, one zone, * one i-edge (i=constant edge) and one j-edge (j=constant edge): * * (ij-1)-------(ij)-------(ij) * | | * | | * | | * (ij-1) (ij) (ij) * | | * | | * | | * (ij-iX-1)----(ij-iX)----(ij-iX) * * At each point, the function value is either 0, 1, or 2, depending * on whether it is below z0, between z0 and z1, or above z1. * Each zone either exists (1) or not (0). * From these three bits of data, all of the curve connectivity follows. * * The tracing algorithm is naturally edge-based: Either you are at a * point where a level cuts an edge, ready to step across a zone to * another edge, or you are drawing the edge itself, if it happens to * be a boundary with at least one section between z0 and z1. * * In either case, the edge is a directed edge -- either the zone * you are advancing into is to its left or right, or you are actually * drawing it. I always trace curves keeping the region between z0 and * z1 to the left of the curve. If I'm tracing a boundary, I'm always * moving CCW (counter clockwise) around the zone that exists. And if * I'm about to cross a zone, I'll make the direction of the edge I'm * sitting on be such that the zone I'm crossing is to its left. * * I start tracing each curve near its lower left corner (mesh oriented * as above), which is the first point I encounter scanning through the * mesh in order. When I figure the 012 z values and zonal existence, * I also mark the potential starting points: Each edge may harbor a * potential starting point corresponding to either direction, so there * are four start possibilities at each ij point. Only the following * possibilities need to be marked as potential starting edges: * * +-+-+-+ * | | | | * A-0-C-+ One or both levels cut E and have z=1 above them, and * | EZ| | 0A is cut and either 0C is cut or CD is cut. * +-B-D-+ Or, one or both levels cut E and E is a boundary edge. * | | | | (and Z exists) * +-+-+-+ * * +-+-+-+ * | | | | * +-A-0-C One or both levels cut E and have z=1 below them, and * | |ZE | 0A is cut and either 0C is cut or CD is cut. * +-+-B-D Or, one or both levels cut E and E is a boundary edge. * | | | | (and Z exists) * +-+-+-+ * * +-+-+-+ * | | | | * +-+-+-+ E is a boundary edge, Z exists, at some point on E * | |Z| | lies between the levels. * +-+E+-+ * | | | | * +-+-+-+ * * +-+-+-+ * | | | | * +-+E+-+ E is a boundary edge, Z exists, at some point on E * | |Z| | lies between the levels. * +-+-+-+ * | | | | * +-+-+-+ * * During the first tracing pass, the start mark is erased whenever * any non-starting edge is encountered, reducing the number of points * that need to be considered for the second pass. The first pass * makes the basic connectivity decisions. It figures out how many * disjoint curves there will be, and identifies slits for the two level * case or open contours for the single level case, and removes all but * the actual start markers. A second tracing pass can perform the * actual final trace. */ /* ------------------------------------------------------------------------ */ /* the data about edges, zones, and points -- boundary or not, exists * or not, z value 0, 1, or 2 -- is kept in a mesh sized data array */ typedef short Cdata; /* here is the minimum structure required to tell where we are in the * mesh sized data array */ typedef struct Csite Csite; struct Csite { long edge; /* ij of current edge */ long left; /* +-1 or +-imax as the zone is to right, left, below, * or above the edge */ long imax; /* imax for the mesh */ long jmax; /* jmax for the mesh */ long n; /* number of points marked on this curve so far */ long count; /* count of start markers visited */ double zlevel[2]; /* contour levels, zlevel[1]<=zlevel[0] * signals single level case */ short *triangle; /* triangulation array for the mesh */ char *reg; /* region array for the mesh (was int) */ Cdata *data; /* added by EF */ long edge0, left0; /* starting site on this curve for closure */ int level0; /* starting level for closure */ long edge00; /* site needing START_ROW mark */ /* making the actual marks requires a bunch of other stuff */ const double *x, *y, *z; /* mesh coordinates and function values */ double *xcp, *ycp; /* output contour points */ }; void print_Csite(Csite *Csite) { Cdata *data = Csite->data; int i, j, ij; int nd = Csite->imax * (Csite->jmax + 1) + 1; printf("zlevels: %8.2lg %8.2lg\n", Csite->zlevel[0], Csite->zlevel[1]); printf("edge %ld, left %ld, n %ld, count %ld, edge0 %ld, left0 %ld\n", Csite->edge, Csite->left, Csite->n, Csite->count, Csite->edge0, Csite->left0); printf(" level0 %d, edge00 %ld\n", Csite->level0, Csite->edge00); printf("%04x\n", data[nd-1]); for (j = Csite->jmax; j >= 0; j--) { for (i=0; i < Csite->imax; i++) { ij = i + j * Csite->imax; printf("%04x ", data[ij]); } printf("\n"); } printf("\n"); } /* triangle only takes values of -1, 0, 1, so it could be a signed char. */ /* most or all of the longs probably could be converted to ints with no loss */ /* the Cdata array consists of the following bits: * Z_VALUE (2 bits) 0, 1, or 2 function value at point * ZONE_EX 1 zone exists, 0 zone doesn't exist * I_BNDY this i-edge (i=constant edge) is a mesh boundary * J_BNDY this j-edge (i=constant edge) is a mesh boundary * I0_START this i-edge is a start point into zone to left * I1_START this i-edge is a start point into zone to right * J0_START this j-edge is a start point into zone below * J1_START this j-edge is a start point into zone above * START_ROW next start point is in current row (accelerates 2nd pass) * SLIT_UP marks this i-edge as the beginning of a slit upstroke * SLIT_DN marks this i-edge as the beginning of a slit downstroke * OPEN_END marks an i-edge start point whose other endpoint is * on a boundary for the single level case * ALL_DONE marks final start point */ #define Z_VALUE 0x0003 #define ZONE_EX 0x0004 #define I_BNDY 0x0008 #define J_BNDY 0x0010 #define I0_START 0x0020 #define I1_START 0x0040 #define J0_START 0x0080 #define J1_START 0x0100 #define START_ROW 0x0200 #define SLIT_UP 0x0400 #define SLIT_DN 0x0800 #define OPEN_END 0x1000 #define ALL_DONE 0x2000 /* some helpful macros to find points relative to a given directed * edge -- points are designated 0, 1, 2, 3 CCW around zone with 0 and * 1 the endpoints of the current edge */ #define FORWARD(left,ix) ((left)>0?((left)>1?1:-(ix)):((left)<-1?-1:(ix))) #define POINT0(edge,fwd) ((edge)-((fwd)>0?fwd:0)) #define POINT1(edge,fwd) ((edge)+((fwd)<0?fwd:0)) #define IS_JEDGE(edge,left) ((left)>0?((left)>1?1:0):((left)<-1?1:0)) #define ANY_START (I0_START|I1_START|J0_START|J1_START) #define START_MARK(left) \ ((left)>0?((left)>1?J1_START:I1_START):((left)<-1?J0_START:I0_START)) /* ------------------------------------------------------------------------ */ /* these actually mark points */ static int zone_crosser (Csite * site, int level, int pass2); static int edge_walker (Csite * site, int pass2); static int slit_cutter (Csite * site, int up, int pass2); /* this calls the first three to trace the next disjoint curve * -- return value is number of points on this curve, or * 0 if there are no more curves this pass * -(number of points) on first pass if: * this is two level case, and the curve closed on a hole * this is single level case, curve is open, and will start from * a different point on the second pass * -- in both cases, this curve will be combined with another * on the second pass */ static long curve_tracer (Csite * site, int pass2); /* this initializes the data array for curve_tracer */ static void data_init (Csite * site, int region, long nchunk); /* ------------------------------------------------------------------------ */ /* zone_crosser assumes you are sitting at a cut edge about to cross * the current zone. It always marks the initial point, crosses at * least one zone, and marks the final point. On non-boundary i-edges, * it is responsible for removing start markers on the first pass. */ static int zone_crosser (Csite * site, int level, int pass2) { Cdata * data = site->data; long edge = site->edge; long left = site->left; long n = site->n; long fwd = FORWARD (left, site->imax); long p0, p1; int jedge = IS_JEDGE (edge, left); long edge0 = site->edge0; long left0 = site->left0; int level0 = site->level0 == level; int two_levels = site->zlevel[1] > site->zlevel[0]; short *triangle = site->triangle; const double *x = pass2 ? site->x : 0; const double *y = pass2 ? site->y : 0; const double *z = pass2 ? site->z : 0; double zlevel = pass2 ? site->zlevel[level] : 0.0; double *xcp = pass2 ? site->xcp : 0; double *ycp = pass2 ? site->ycp : 0; int z0, z1, z2, z3; int keep_left = 0; /* flag to try to minimize curvature in saddles */ int done = 0; if (level) level = 2; for (;;) { /* set edge endpoints */ p0 = POINT0 (edge, fwd); p1 = POINT1 (edge, fwd); /* always mark cut on current edge */ if (pass2) { /* second pass actually computes and stores the point */ double zcp = (zlevel - z[p0]) / (z[p1] - z[p0]); xcp[n] = zcp * (x[p1] - x[p0]) + x[p0]; ycp[n] = zcp * (y[p1] - y[p0]) + y[p0]; } if (!done && !jedge) { if (n) { /* if this is not the first point on the curve, and we're * not done, and this is an i-edge, check several things */ if (!two_levels && !pass2 && (data[edge] & OPEN_END)) { /* reached an OPEN_END mark, skip the n++ */ done = 4; /* same return value 4 used below */ break; } /* check for curve closure -- if not, erase any start mark */ if (edge == edge0 && left == left0) { /* may signal closure on a downstroke */ if (level0) done = (!pass2 && two_levels && left < 0) ? 5 : 3; } else if (!pass2) { Cdata start = data[edge] & (fwd > 0 ? I0_START : I1_START); if (start) { data[edge] &= ~start; site->count--; } if (!two_levels) { start = data[edge] & (fwd > 0 ? I1_START : I0_START); if (start) { data[edge] &= ~start; site->count--; } } } } } n++; if (done) break; /* cross current zone to another cut edge */ z0 = (data[p0] & Z_VALUE) != level; /* 1 if fill toward p0 */ z1 = !z0; /* know level cuts edge */ z2 = (data[p1 + left] & Z_VALUE) != level; z3 = (data[p0 + left] & Z_VALUE) != level; if (z0 == z2) { if (z1 == z3) { /* this is a saddle zone, need triangle to decide * -- set triangle if not already decided for this zone */ long zone = edge + (left > 0 ? left : 0); if (triangle) { if (!triangle[zone]) { if (keep_left) triangle[zone] = jedge ? -1 : 1; else triangle[zone] = jedge ? 1 : -1; } if (triangle[zone] > 0 ? !jedge : jedge) goto bkwd; } else { if (keep_left) goto bkwd; } } /* bend forward (right along curve) */ keep_left = 1; jedge = !jedge; edge = p1 + (left > 0 ? left : 0); { long tmp = fwd; fwd = -left; left = tmp; } } else if (z1 == z3) { bkwd: /* bend backward (left along curve) */ keep_left = 0; jedge = !jedge; edge = p0 + (left > 0 ? left : 0); { long tmp = fwd; fwd = left; left = -tmp; } } else { /* straight across to opposite edge */ edge += left; } /* after crossing zone, edge/left/fwd is oriented CCW relative to * the next zone, assuming we will step there */ /* now that we've taken a step, check for the downstroke * of a slit on the second pass (upstroke checked above) * -- taking step first avoids a race condition */ if (pass2 && two_levels && !jedge) { if (left > 0) { if (data[edge] & SLIT_UP) done = 6; } else { if (data[edge] & SLIT_DN) done = 5; } } if (!done) { /* finally, check if we are on a boundary */ if (data[edge] & (jedge ? J_BNDY : I_BNDY)) { done = two_levels ? 2 : 4; /* flip back into the zone that exists */ left = -left; fwd = -fwd; if (!pass2 && (edge != edge0 || left != left0)) { Cdata start = data[edge] & START_MARK (left); if (start) { data[edge] &= ~start; site->count--; } } } } } site->edge = edge; site->n = n; site->left = left; return done > 4 ? slit_cutter (site, done - 5, pass2) : done; } /* edge_walker assumes that the current edge is being drawn CCW * around the current zone. Since only boundary edges are drawn * and we always walk around with the filled region to the left, * no edge is ever drawn CW. We attempt to advance to the next * edge on this boundary, but if current second endpoint is not * between the two contour levels, we exit back to zone_crosser. * Note that we may wind up marking no points. * -- edge_walker is never called for single level case */ static int edge_walker (Csite * site, int pass2) { Cdata * data = site->data; long edge = site->edge; long left = site->left; long n = site->n; long fwd = FORWARD (left, site->imax); long p0 = POINT0 (edge, fwd); long p1 = POINT1 (edge, fwd); int jedge = IS_JEDGE (edge, left); long edge0 = site->edge0; long left0 = site->left0; int level0 = site->level0 == 2; int marked; const double *x = pass2 ? site->x : 0; const double *y = pass2 ? site->y : 0; double *xcp = pass2 ? site->xcp : 0; double *ycp = pass2 ? site->ycp : 0; int z0, z1, heads_up = 0; for (;;) { /* mark endpoint 0 only if value is 1 there, and this is a * two level task */ z0 = data[p0] & Z_VALUE; z1 = data[p1] & Z_VALUE; marked = 0; if (z0 == 1) { /* mark current boundary point */ if (pass2) { xcp[n] = x[p0]; ycp[n] = y[p0]; } marked = 1; } else if (!n) { /* if this is the first point is not between the levels * must do the job of the zone_crosser and mark the first cut here, * so that it will be marked again by zone_crosser as it closes */ if (pass2) { double zcp = site->zlevel[(z0 != 0)]; zcp = (zcp - site->z[p0]) / (site->z[p1] - site->z[p0]); xcp[n] = zcp * (x[p1] - x[p0]) + x[p0]; ycp[n] = zcp * (y[p1] - y[p0]) + y[p0]; } marked = 1; } if (n) { /* check for closure */ if (level0 && edge == edge0 && left == left0) { site->edge = edge; site->left = left; site->n = n + marked; /* if the curve is closing on a hole, need to make a downslit */ if (fwd < 0 && !(data[edge] & (jedge ? J_BNDY : I_BNDY))) return slit_cutter (site, 0, pass2); return 3; } else if (pass2) { if (heads_up || (fwd < 0 && (data[edge] & SLIT_DN))) { site->edge = edge; site->left = left; site->n = n + marked; return slit_cutter (site, heads_up, pass2); } } else { /* if this is not first point, clear start mark for this edge */ Cdata start = data[edge] & START_MARK (left); if (start) { data[edge] &= ~start; site->count--; } } } if (marked) n++; /* if next endpoint not between levels, need to exit to zone_crosser */ if (z1 != 1) { site->edge = edge; site->left = left; site->n = n; return (z1 != 0); /* return level closest to p1 */ } /* step to p1 and find next edge * -- turn left if possible, else straight, else right * -- check for upward slit beginning at same time */ edge = p1 + (left > 0 ? left : 0); if (pass2 && jedge && fwd > 0 && (data[edge] & SLIT_UP)) { jedge = !jedge; heads_up = 1; } else if (data[edge] & (jedge ? I_BNDY : J_BNDY)) { long tmp = fwd; fwd = left; left = -tmp; jedge = !jedge; } else { edge = p1 + (fwd > 0 ? fwd : 0); if (pass2 && !jedge && fwd > 0 && (data[edge] & SLIT_UP)) { heads_up = 1; } else if (!(data[edge] & (jedge ? J_BNDY : I_BNDY))) { edge = p1 - (left < 0 ? left : 0); jedge = !jedge; { long tmp = fwd; fwd = -left; left = tmp; } } } p0 = p1; p1 = POINT1 (edge, fwd); } } /* -- slit_cutter is never called for single level case */ static int slit_cutter (Csite * site, int up, int pass2) { Cdata * data = site->data; long imax = site->imax; long n = site->n; const double *x = pass2 ? site->x : 0; const double *y = pass2 ? site->y : 0; double *xcp = pass2 ? site->xcp : 0; double *ycp = pass2 ? site->ycp : 0; if (up) { /* upward stroke of slit proceeds up left side of slit until * it hits a boundary or a point not between the contour levels * -- this never happens on the first pass */ long p1 = site->edge; int z1; for (;;) { z1 = data[p1] & Z_VALUE; if (z1 != 1) { site->edge = p1; site->left = -1; site->n = n; return (z1 != 0); } else if (data[p1] & J_BNDY) { /* this is very unusual case of closing on a mesh hole */ site->edge = p1; site->left = -imax; site->n = n; return 2; } xcp[n] = x[p1]; ycp[n] = y[p1]; n++; p1 += imax; } } else { /* downward stroke proceeds down right side of slit until it * hits a boundary or point not between the contour levels */ long p0 = site->edge; int z0; /* at beginning of first pass, mark first i-edge with SLIT_DN */ data[p0] |= SLIT_DN; p0 -= imax; for (;;) { z0 = data[p0] & Z_VALUE; if (!pass2) { if (z0 != 1 || (data[p0] & I_BNDY) || (data[p0 + 1] & J_BNDY)) { /* at end of first pass, mark final i-edge with SLIT_UP */ data[p0 + imax] |= SLIT_UP; /* one extra count for splicing at outer curve */ site->n = n + 1; return 4; /* return same special value as for OPEN_END */ } } else { if (z0 != 1) { site->edge = p0 + imax; site->left = 1; site->n = n; return (z0 != 0); } else if (data[p0 + 1] & J_BNDY) { site->edge = p0 + 1; site->left = imax; site->n = n; return 2; } else if (data[p0] & I_BNDY) { site->edge = p0; site->left = 1; site->n = n; return 2; } } if (pass2) { xcp[n] = x[p0]; ycp[n] = y[p0]; n++; } else { /* on first pass need to count for upstroke as well */ n += 2; } p0 -= imax; } } } /* ------------------------------------------------------------------------ */ /* curve_tracer finds the next starting point, then traces the curve, * returning the number of points on this curve * -- in a two level trace, the return value is negative on the * first pass if the curve closed on a hole * -- in a single level trace, the return value is negative on the * first pass if the curve is an incomplete open curve * -- a return value of 0 indicates no more curves */ static long curve_tracer (Csite * site, int pass2) { Cdata * data = site->data; long imax = site->imax; long edge0 = site->edge0; long left0 = site->left0; long edge00 = site->edge00; int two_levels = site->zlevel[1] > site->zlevel[0]; int level, level0, mark_row; long n; /* it is possible for a single i-edge to serve as two actual start * points, one to the right and one to the left * -- for the two level case, this happens on the first pass for * a doubly cut edge, or on a chunking boundary * -- for single level case, this is impossible, but a similar * situation involving open curves is handled below * a second two start possibility is when the edge0 zone does not * exist and both the i-edge and j-edge boundaries are cut * yet another possibility is three start points at a junction * of chunk cuts * -- sigh, several other rare possibilities, * allow for general case, just go in order i1, i0, j1, j0 */ int two_starts; /* printf("curve_tracer pass %d\n", pass2); */ /* print_Csite(site); */ if (left0 == 1) two_starts = data[edge0] & (I0_START | J1_START | J0_START); else if (left0 == -1) two_starts = data[edge0] & (J1_START | J0_START); else if (left0 == imax) two_starts = data[edge0] & J0_START; else two_starts = 0; if (pass2 || edge0 == 0) { /* zip up to row marked on first pass (or by data_init if edge0==0) * -- but not for double start case */ if (!two_starts) { /* final start point marked by ALL_DONE marker */ int first = (edge0 == 0 && !pass2); long e0 = edge0; if (data[edge0] & ALL_DONE) return 0; while (!(data[edge0] & START_ROW)) edge0 += imax; if (e0 == edge0) edge0++; /* two starts handled specially */ if (first) /* if this is the very first start point, we want to remove * the START_ROW marker placed by data_init */ data[edge0 - edge0 % imax] &= ~START_ROW; } } else { /* first pass ends when all potential start points visited */ if (site->count <= 0) { /* place ALL_DONE marker for second pass */ data[edge00] |= ALL_DONE; /* reset initial site for second pass */ site->edge0 = site->edge00 = site->left0 = 0; return 0; } if (!two_starts) edge0++; } if (two_starts) { /* trace second curve with this start immediately */ if (left0 == 1 && (data[edge0] & I0_START)) { left0 = -1; level = (data[edge0] & I_BNDY) ? 2 : 0; } else if ((left0 == 1 || left0 == -1) && (data[edge0] & J1_START)) { left0 = imax; level = 2; } else { left0 = -imax; level = 2; } } else { /* usual case is to scan for next start marker * -- on second pass, this is at most one row of mesh, but first * pass hits nearly every point of the mesh, since it can't * know in advance which potential start marks removed */ while (!(data[edge0] & ANY_START)) edge0++; if (data[edge0] & I1_START) left0 = 1; else if (data[edge0] & I0_START) left0 = -1; else if (data[edge0] & J1_START) left0 = imax; else /*data[edge0]&J0_START */ left0 = -imax; if (data[edge0] & (I1_START | I0_START)) level = (data[edge0] & I_BNDY) ? 2 : 0; else level = 2; } /* this start marker will not be unmarked, but it has been visited */ if (!pass2) site->count--; /* if this curve starts on a non-boundary i-edge, we need to * determine the level */ if (!level && two_levels) level = left0 > 0 ? ((data[edge0 - imax] & Z_VALUE) != 0) : ((data[edge0] & Z_VALUE) != 0); /* initialize site for this curve */ site->edge = site->edge0 = edge0; site->left = site->left0 = left0; site->level0 = level0 = level; /* for open curve detection only */ /* single level case just uses zone_crosser */ if (!two_levels) level = 0; /* to generate the curve, alternate between zone_crosser and * edge_walker until closure or first call to edge_walker in * single level case */ site->n = 0; for (;;) { if (level < 2) level = zone_crosser (site, level, pass2); else if (level < 3) level = edge_walker (site, pass2); else break; } n = site->n; /* single level case may have ended at a boundary rather than closing * -- need to recognize this case here in order to place the * OPEN_END mark for zone_crosser, remove this start marker, * and be sure not to make a START_ROW mark for this case * two level case may close with slit_cutter, in which case start * must also be removed and no START_ROW mark made * -- change sign of return n to inform caller */ if (!pass2 && level > 3 && (two_levels || level0 == 0)) { if (!two_levels) data[edge0] |= OPEN_END; data[edge0] &= ~(left0 > 0 ? I1_START : I0_START); mark_row = 0; /* do not mark START_ROW */ n = -n; } else { if (two_levels) mark_row = !two_starts; else mark_row = 1; } /* on first pass, must apply START_ROW mark in column above previous * start marker * -- but skip if we just did second of two start case */ if (!pass2 && mark_row) { data[edge0 - (edge0 - edge00) % imax] |= START_ROW; site->edge00 = edge0; } return n; } /* ------------------------------------------------------------------------ */ /* The sole function of the "region" argument is to specify the value in Csite.reg that denotes a missing zone. We always use zero. */ static void data_init (Csite * site, int region, long nchunk) { Cdata * data = site->data; long imax = site->imax; long jmax = site->jmax; long ijmax = imax * jmax; const double *z = site->z; double zlev0 = site->zlevel[0]; double zlev1 = site->zlevel[1]; int two_levels = zlev1 > zlev0; char *reg = site->reg; long count = 0; int started = 0; int ibndy, jbndy, i_was_chunk; long icsize = imax - 1; long jcsize = jmax - 1; long ichunk, jchunk, irem, jrem, i, j, ij; if (nchunk && two_levels) { /* figure out chunk sizes * -- input nchunk is square root of maximum allowed zones per chunk * -- start points for single level case are wrong, so don't try it */ long inum = (nchunk * nchunk) / (jmax - 1); long jnum = (nchunk * nchunk) / (imax - 1); if (inum < nchunk) inum = nchunk; if (jnum < nchunk) jnum = nchunk; /* ijnum= actual number of chunks, * ijrem= number of those chunks needing one more zone (ijcsize+1) */ inum = (imax - 2) / inum + 1; icsize = (imax - 1) / inum; irem = (imax - 1) % inum; jnum = (jmax - 2) / jnum + 1; jcsize = (jmax - 1) / jnum; jrem = (jmax - 1) % jnum; /* convert ijrem into value of i or j at which to begin adding an * extra zone */ irem = (inum - irem) * icsize; jrem = (jnum - jrem) * jcsize; } else { irem = imax; jrem = jmax; } /* do everything in a single pass through the data array to * minimize cache faulting (z, reg, and data are potentially * very large arrays) * access to the z and reg arrays is strictly sequential, * but we need two rows (+-imax) of the data array at a time */ if (z[0] > zlev0) data[0] = (two_levels && z[0] > zlev1) ? 2 : 1; else data[0] = 0; jchunk = 0; for (j = ij = 0; j < jmax; j++) { ichunk = i_was_chunk = 0; for (i = 0; i < imax; i++, ij++) { /* transfer zonal existence from reg to data array * -- get these for next row so we can figure existence of * points and j-edges for this row */ data[ij + imax + 1] = 0; if (reg) { if (region ? (reg[ij + imax + 1] == region) : (reg[ij + imax + 1] != 0)) data[ij + imax + 1] = ZONE_EX; } else { if (i < imax - 1 && j < jmax - 1) data[ij + imax + 1] = ZONE_EX; } /* translate z values to 0, 1, 2 flags */ if (ij < imax) data[ij + 1] = 0; if (ij < ijmax - 1 && z[ij + 1] > zlev0) data[ij + 1] |= (two_levels && z[ij + 1] > zlev1) ? 2 : 1; /* apply edge boundary marks */ ibndy = i == ichunk || (data[ij] & ZONE_EX) != (data[ij + 1] & ZONE_EX); jbndy = j == jchunk || (data[ij] & ZONE_EX) != (data[ij + imax] & ZONE_EX); if (ibndy) data[ij] |= I_BNDY; if (jbndy) data[ij] |= J_BNDY; /* apply i-edge start marks * -- i-edges are only marked when actually cut * -- no mark is necessary if one of the j-edges which share * the lower endpoint is also cut * -- no I0 mark necessary unless filled region below some cut, * no I1 mark necessary unless filled region above some cut */ if (j) { int v0 = (data[ij] & Z_VALUE); int vb = (data[ij - imax] & Z_VALUE); if (v0 != vb) { /* i-edge is cut */ if (ibndy) { if (data[ij] & ZONE_EX) { data[ij] |= I0_START; count++; } if (data[ij + 1] & ZONE_EX) { data[ij] |= I1_START; count++; } } else { int va = (data[ij - 1] & Z_VALUE); int vc = (data[ij + 1] & Z_VALUE); int vd = (data[ij - imax + 1] & Z_VALUE); if (v0 != 1 && va != v0 && (vc != v0 || vd != v0) && (data[ij] & ZONE_EX)) { data[ij] |= I0_START; count++; } if (vb != 1 && va == vb && (vc == vb || vd == vb) && (data[ij + 1] & ZONE_EX)) { data[ij] |= I1_START; count++; } } } } /* apply j-edge start marks * -- j-edges are only marked when they are boundaries * -- all cut boundary edges marked * -- for two level case, a few uncut edges must be marked */ if (i && jbndy) { int v0 = (data[ij] & Z_VALUE); int vb = (data[ij - 1] & Z_VALUE); if (v0 != vb) { if (data[ij] & ZONE_EX) { data[ij] |= J0_START; count++; } if (data[ij + imax] & ZONE_EX) { data[ij] |= J1_START; count++; } } else if (two_levels && v0 == 1) { if (data[ij + imax] & ZONE_EX) { if (i_was_chunk || !(data[ij + imax - 1] & ZONE_EX)) { /* lower left is a drawn part of boundary */ data[ij] |= J1_START; count++; } } else if (data[ij] & ZONE_EX) { if (data[ij + imax - 1] & ZONE_EX) { /* weird case of open hole at lower left */ data[ij] |= J0_START; count++; } } } } i_was_chunk = (i == ichunk); if (i_was_chunk) ichunk += icsize + (ichunk >= irem); } if (j == jchunk) jchunk += jcsize + (jchunk >= jrem); /* place first START_ROW marker */ if (count && !started) { data[ij - imax] |= START_ROW; started = 1; } } /* place immediate stop mark if nothing found */ if (!count) data[0] |= ALL_DONE; /* initialize site */ site->edge0 = site->edge00 = site->edge = 0; site->left0 = site->left = 0; site->n = 0; site->count = count; } /* ------------------------------------------------------------------------ Original (slightly modified) core contour generation routines are above; below are new routines for interfacing to mpl. ------------------------------------------------------------------------ */ /* Note: index order gets switched in the Python interface; python Z[i,j] -> C z[j,i] so if the array has shape Mi, Nj in python, we have iMax = Nj, jMax = Mi in gcntr.c. On the Python side: Ny, Nx = shape(z), so in C, the x-dimension is the first index, the y-dimension the second. */ /* reg should have the same dimensions as data, which has an extra iMax + 1 points relative to Z. It differs from mask in being the opposite (True where a region exists, versus the mask, which is True where a data point is bad), and in that it marks zones, not points. All four zones sharing a bad point must be marked as not existing. */ void mask_zones (long iMax, long jMax, char *mask, char *reg) { long i, j, ij; long nreg = iMax * jMax + iMax + 1; for (ij = iMax+1; ij < iMax*jMax; ij++) { reg[ij] = 1; } ij = 0; for (j = 0; j < jMax; j++) { for (i = 0; i < iMax; i++, ij++) { if (i == 0 || j == 0) reg[ij] = 0; if (mask[ij] != 0) { reg[ij] = 0; reg[ij + 1] = 0; reg[ij + iMax] = 0; reg[ij + iMax + 1] = 0; } } } for (; ij < nreg; ij++) { reg[ij] = 0; } } static Csite * cntr_new(void) { Csite *site; site = (Csite *) PyMem_Malloc(sizeof(Csite)); if (site == NULL) return NULL; site->data = NULL; site->reg = NULL; site->triangle = NULL; site->xcp = NULL; site->ycp = NULL; site->x = NULL; site->y = NULL; site->z = NULL; return site; } static int cntr_init(Csite *site, long iMax, long jMax, double *x, double *y, double *z, char *mask) { long ijmax = iMax * jMax; long nreg = iMax * jMax + iMax + 1; long i; site->imax = iMax; site->jmax = jMax; site->data = (Cdata *) PyMem_Malloc(sizeof(Cdata) * nreg); if (site->data == NULL) { PyMem_Free(site); return -1; } site->triangle = (short *) PyMem_Malloc(sizeof(short) * ijmax); if (site->triangle == NULL) { PyMem_Free(site->data); PyMem_Free(site); return -1; } for (i = 0; i < ijmax; i++) site->triangle[i] = 0; site->reg = NULL; if (mask != NULL) { site->reg = (char *) PyMem_Malloc(sizeof(char) * nreg); if (site->reg == NULL) { PyMem_Free(site->triangle); PyMem_Free(site->data); PyMem_Free(site); return -1; } mask_zones(iMax, jMax, mask, site->reg); } /* I don't think we need to initialize site->data. */ site->x = x; site->y = y; site->z = z; site->xcp = NULL; site->ycp = NULL; return 0; } void cntr_del(Csite *site) { PyMem_Free(site->triangle); PyMem_Free(site->reg); PyMem_Free(site->data); PyMem_Free(site); site = NULL; } /* Build a list of lists of points, where each point is an (x,y) tuple. */ static PyObject * build_cntr_list_p(long *np, double *xp, double *yp, int nparts, long ntotal) { PyObject *point, *contourList, *all_contours; int start = 0, end = 0; int i, j, k; all_contours = PyList_New(nparts); for (i = 0; i < nparts; i++) { start = end; end += np[i]; contourList = PyList_New(np[i]); for (k = 0, j = start; j < end; j++, k++) { point = Py_BuildValue("(dd)", xp[j], yp[j]); if (PyList_SetItem(contourList, k, point)) goto error; } if (PyList_SetItem(all_contours, i, contourList)) goto error; } return all_contours; error: Py_XDECREF(all_contours); return NULL; } /* Build a list of tuples (X, Y), where X and Y are 1-D arrays. */ static PyObject * build_cntr_list_v(long *np, double *xp, double *yp, int nparts, long ntotal) { PyObject *point, *all_contours; PyArrayObject *xv, *yv; int dims[1]; int i; long j, k; all_contours = PyList_New(nparts); k = 0; for (i = 0; i < nparts; i++) { dims[0] = np[i]; xv = (PyArrayObject *) PyArray_FromDims(1, dims, 'd'); yv = (PyArrayObject *) PyArray_FromDims(1, dims, 'd'); if (xv == NULL || yv == NULL) goto error; for (j = 0; j < dims[0]; j++) { ((double *)xv->data)[j] = xp[k]; ((double *)yv->data)[j] = yp[k]; k++; } point = Py_BuildValue("(NN)", xv, yv); /* "O" increments ref count; "N" does not. */ if (PyList_SetItem(all_contours, i, point)) goto error; } return all_contours; error: Py_XDECREF(all_contours); return NULL; } /* cntr_trace is called once per contour level or level pair. If nlevels is 1, a set of contour lines will be returned; if nlevels is 2, the set of polygons bounded by the levels will be returned. If points is True, the lines will be returned as a list of list of points; otherwise, as a list of tuples of vectors. */ PyObject * cntr_trace(Csite *site, double levels[], int nlevels, int points) { PyObject *c_list; double *xp0; double *yp0; long *nseg0; int iseg; long nchunk = 300; /* hardwired for now */ long n; long nparts = 0; long ntotal = 0; long nparts2 = 0; long ntotal2 = 0; site->zlevel[0] = levels[0]; site->zlevel[1] = levels[0]; if (nlevels == 2) { site->zlevel[1] = levels[1]; } site->n = site->count = 0; data_init (site, 0, nchunk); /* make first pass to compute required sizes for second pass */ for (;;) { n = curve_tracer (site, 0); if (!n) break; if (n > 0) { nparts++; ntotal += n; } else { ntotal -= n; } } xp0 = (double *) PyMem_Malloc(ntotal * sizeof(double)); yp0 = (double *) PyMem_Malloc(ntotal * sizeof(double)); nseg0 = (long *) PyMem_Malloc(nparts * sizeof(long)); if (xp0 == NULL || yp0 == NULL || nseg0 == NULL) goto error; /* second pass */ site->xcp = xp0; site->ycp = yp0; iseg = 0; for (;;iseg++) { n = curve_tracer (site, 1); if (ntotal2 + n > ntotal) { PyErr_SetString(PyExc_RuntimeError, "curve_tracer: ntotal2, pass 2 exceeds ntotal, pass 1"); goto error; } if (n == 0) break; if (n > 0) { /* could add array bounds checking */ nseg0[iseg] = n; site->xcp += n; site->ycp += n; ntotal2 += n; nparts2++; } else { PyErr_SetString(PyExc_RuntimeError, "Negative n from curve_tracer in pass 2"); goto error; } } if (points) { c_list = build_cntr_list_p(nseg0, xp0, yp0, nparts, ntotal); } else { c_list = build_cntr_list_v(nseg0, xp0, yp0, nparts, ntotal); } PyMem_Free(xp0); PyMem_Free(yp0); PyMem_Free(nseg0); site->xcp = NULL; site->ycp = NULL; return c_list; error: PyMem_Free(xp0); PyMem_Free(yp0); PyMem_Free(nseg0); site->xcp = NULL; site->ycp = NULL; return NULL; } /******* Make an extension type. Based on the tutorial.************/ /* site points to the data arrays in the arrays pointed to by xpa, ypa, zpa, and mpa, so we include them in the structure so we can ensure they are not deleted until we have finished using them. */ typedef struct { PyObject_HEAD PyArrayObject *xpa, *ypa, *zpa, *mpa; Csite *site; } Cntr; static int Cntr_clear(Cntr* self) { PyArrayObject *tmp; cntr_del(self->site); tmp = self->xpa; self->xpa = NULL; Py_XDECREF(tmp); tmp = self->ypa; self->ypa = NULL; Py_XDECREF(tmp); tmp = self->zpa; self->zpa = NULL; Py_XDECREF(tmp); tmp = self->mpa; self->mpa = NULL; Py_XDECREF(tmp); return 0; } static void Cntr_dealloc(Cntr* self) { Cntr_clear(self); self->ob_type->tp_free((PyObject*)self); } static PyObject * Cntr_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { Cntr *self; self = (Cntr *)type->tp_alloc(type, 0); if (self != NULL) { self->site = cntr_new(); if (self->site == NULL) { PyErr_SetString(PyExc_MemoryError, "Memory allocation failed in cntr_new."); Py_XDECREF(self); return NULL; } self->xpa = NULL; self->ypa = NULL; self->zpa = NULL; self->mpa = NULL; } return (PyObject *)self; } static int Cntr_init(Cntr *self, PyObject *args, PyObject *kwds) { PyObject *xarg, *yarg, *zarg, *marg; PyArrayObject *xpa, *ypa, *zpa, *mpa; long iMax, jMax; char *mask; static char *kwlist[] = {"x", "y", "z", "mask", NULL}; marg = NULL; if (! PyArg_ParseTupleAndKeywords(args, kwds, "OOO|O", kwlist, &xarg, &yarg, &zarg, &marg)) return -1; if (marg == Py_None) marg = NULL; if (!PyArray_Check(xarg) || !PyArray_Check(yarg) || !PyArray_Check(zarg) || (marg && !PyArray_Check(marg))) { PyErr_SetString(PyExc_TypeError, "Arguments x, y, z, (optional) mask must be arrays."); return -1; } xpa = (PyArrayObject *) PyArray_ContiguousFromObject(xarg, 'd', 2, 2); ypa = (PyArrayObject *) PyArray_ContiguousFromObject(yarg, 'd', 2, 2); zpa = (PyArrayObject *) PyArray_ContiguousFromObject(zarg, 'd', 2, 2); if (marg) mpa = (PyArrayObject *) PyArray_ContiguousFromObject(marg, '1', 2, 2); else mpa = NULL; if (xpa == NULL || ypa == NULL || zpa == NULL || (marg && mpa == NULL)) { PyErr_SetString(PyExc_ValueError, "Arguments x, y, z, mask (if present) must be 2D arrays."); goto error; } iMax = zpa->dimensions[1]; jMax = zpa->dimensions[0]; if (xpa->dimensions[0] != jMax || xpa->dimensions[1] != iMax || ypa->dimensions[0] != jMax || ypa->dimensions[1] != iMax || (mpa && (mpa->dimensions[0] != jMax || mpa->dimensions[1] != iMax))) { PyErr_SetString(PyExc_ValueError, "Arguments x, y, z, mask (if present)" " must have the same dimensions."); goto error; } if (mpa) mask = mpa->data; else mask = NULL; if ( cntr_init(self->site, iMax, jMax, (double *)xpa->data, (double *)ypa->data, (double *)zpa->data, mask)) { PyErr_SetString(PyExc_MemoryError, "Memory allocation failure in cntr_init"); goto error; } self->xpa = xpa; self->ypa = ypa; self->zpa = zpa; self->mpa = mpa; return 0; error: Py_XDECREF(xpa); Py_XDECREF(ypa); Py_XDECREF(zpa); Py_XDECREF(mpa); return -1; } static PyObject * Cntr_trace(Cntr *self, PyObject *args, PyObject *kwds) { double levels[2] = {0.0, -1e100}; int nlevels = 2; int points = 0; static char *kwlist[] = {"level0", "level1", "points", NULL}; if (! PyArg_ParseTupleAndKeywords(args, kwds, "d|di", kwlist, levels, levels+1, &points)) { return NULL; } if (levels[1] == -1e100 || levels[1] <= levels[0]) nlevels = 1; return cntr_trace(self->site, levels, nlevels, points); } static PyMethodDef Cntr_methods[] = { {"trace", (PyCFunction)Cntr_trace, METH_VARARGS | METH_KEYWORDS, "Return a list of contour line segments or polygons.\n\n" " Required argument: level0, a contour level\n" " Optional argument: level1; if given, and if level1 > level0,\n" " then the contours will be polygons surrounding areas between\n" " the levels.\n" " Optional argument: points; if 0 (default), return a list of\n" " vector pairs; otherwise, return a list of lists of points.\n" }, {NULL} /* Sentinel */ }; static PyTypeObject CntrType = { PyObject_HEAD_INIT(NULL) 0, /*ob_size*/ "cntr.Cntr", /*tp_name*/ sizeof(Cntr), /*tp_basicsize*/ 0, /*tp_itemsize*/ (destructor)Cntr_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash */ 0, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT, /*tp_flags*/ "Contour engine", /* tp_doc */ 0, /* tp_traverse */ (inquiry)Cntr_clear, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ Cntr_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)Cntr_init, /* tp_init */ 0, /* tp_alloc */ Cntr_new, /* tp_new */ }; static PyMethodDef module_methods[] = { {NULL} /* Sentinel */ }; #ifdef NUMARRAY PyMODINIT_FUNC initcontour(void) { PyObject* m; if (PyType_Ready(&CntrType) < 0) return; m = Py_InitModule3("contour", module_methods, "Contouring engine as an extension type (numarray)."); if (m == NULL) return; import_array(); Py_INCREF(&CntrType); PyModule_AddObject(m, "Cntr", (PyObject *)&CntrType); } #else PyMODINIT_FUNC initcontour(void) { PyObject* m; if (PyType_Ready(&CntrType) < 0) return; m = Py_InitModule3("contour", module_methods, "Contouring engine as an extension type (Numeric)."); if (m == NULL) return; import_array(); Py_INCREF(&CntrType); PyModule_AddObject(m, "Cntr", (PyObject *)&CntrType); } #endif chaco-4.5.0/chaco/contour/setup.py0000644000076600000240000000077212426452312017644 0ustar jrocherstaff00000000000000#!/usr/bin/env python def configuration(parent_package='',top_path=None): from numpy.distutils.misc_util import Configuration config = Configuration('contour',parent_package,top_path) numerix_info = config.get_info('numerix') config.add_extension('contour',['*.c'],**numerix_info) config.add_data_dir('tests') return config if __name__ == "__main__": from numpy.distutils.core import setup setup( zip_safe = False, configuration=configuration) chaco-4.5.0/chaco/contour_line_plot.py0000644000076600000240000001641612426452312020553 0ustar jrocherstaff00000000000000""" Defines the ContourLinePlot class. """ from __future__ import with_statement # Major library imports from numpy import array, linspace, meshgrid, transpose # Enthought library imports from enable.api import LineStyle from kiva import constants from traits.api import Bool, Dict, Float, List, Str, Trait # Local relative imports from base_contour_plot import BaseContourPlot from contour.contour import Cntr class ContourLinePlot(BaseContourPlot): """ Takes a value data object whose elements are scalars, and renders them as a contour plot. """ # TODO: Modify ImageData to explicitly support scalar value arrays #------------------------------------------------------------------------ # Data-related traits #------------------------------------------------------------------------ # The thickness(es) of the contour lines. # It can be either a scalar value, valid for all contour lines, or a list # of widths. If the list is too short with respect to then number of # contour lines, the values are repeated from the beginning of the list. # Widths are associated with levels of increasing value. widths = Trait(1.0, Float, List) # The line dash style(s). styles = Trait("signed", Str, List) # Line style for positive levels. positive_style = LineStyle("solid") # Line style for negative levels. negative_style = LineStyle("dash") #------------------------------------------------------------------------ # Private traits #------------------------------------------------------------------------ # Are the cached contours valid? If False, new ones need to be computed. _contour_cache_valid = Bool(False) # Cached collection of traces. _cached_contours = Dict # Is the cached width data valid? _widths_cache_valid = Bool(False) # Is the cached style data valid? _styles_cache_valid = Bool(False) # Cached list of line widths _widths = List # Cached list of line styles _styles = List # Mapped trait used to convert user-supplied line style values to # AGG-acceptable ones. (Mapped traits in lists are not supported, must be # converted one at a time.) _style_map_trait = LineStyle #------------------------------------------------------------------------ # Private methods #------------------------------------------------------------------------ def _render(self, gc): """ Actually draws the plot. Implements the Base2DPlot interface. """ if not self._level_cache_valid: self._update_levels() if not self._contour_cache_valid: self._update_contours() if not self._widths_cache_valid: self._update_widths() if not self._styles_cache_valid: self._update_styles() if not self._colors_cache_valid: self._update_colors() with gc: gc.set_antialias(True) gc.clip_to_rect(self.x, self.y, self.width, self.height) gc.set_alpha(self.alpha) gc.set_line_join(constants.JOIN_BEVEL) gc.set_line_cap(constants.CAP_ROUND) for i in range(len(self._levels)): gc.set_stroke_color(self._colors[i]) gc.set_line_width(self._widths[i]) gc.set_line_dash(self._styles[i]) for trace in self._cached_contours[self._levels[i]]: if self.orientation == "h": strace = self.index_mapper.map_screen(trace) else: strace = array(self.index_mapper.map_screen(trace))[:,::-1] gc.begin_path() gc.lines(strace) gc.stroke_path() def _update_contours(self): """ Updates the cache of contour lines """ # x and y data are "fenceposts" so ignore the last value # XXX: this truncation is causing errors in Cntr() as of r13735 xdata = self.index._xdata.get_data() ydata = self.index._ydata.get_data() xs = linspace(xdata[0], xdata[-1], len(xdata)-1) ys = linspace(ydata[0], ydata[-1], len(ydata)-1) xg, yg = meshgrid(xs, ys) if self.orientation == "h": c = Cntr(xg, yg, self.value.raw_value) else: c = Cntr(xg, yg, self.value.raw_value.T) self._cached_contours = {} for level in self._levels: self._cached_contours[level] = [] traces = c.trace(level) for trace in traces: self._cached_contours[level].append(transpose(trace)) self._contour_cache_valid = True def _update_levels(self): """ Extends the parent method to also invalidate some other things """ super(ContourLinePlot, self)._update_levels() self._contour_cache_valid = False self._widths_cache_valid = False self._styles_cache_valid = False def _update_widths(self): """ Updates the widths cache. """ # If we are given a single width, apply it to all levels if isinstance(self.widths, float): self._widths = [self.widths] * len(self._levels) # If the list of widths is shorter than the list of levels, # simply repeat widths from the beginning of the list as needed else: self._widths = [] for i in range(len(self._levels)): self._widths.append(self.widths[i%len(self.widths)]) self._widths_cache_valid = True def _update_styles(self): """ Updates the styles cache. """ # If the style type is "signed" then assign styles to levels based # on their sign if self.styles == "signed": self._styles = [] for level in self._levels: if level < 0: self._styles.append(self.negative_style_) else: self._styles.append(self.positive_style_) # If we not given a list, apply the one style to all levels elif not isinstance(self.styles, list): self._style_map_trait = self.styles self._styles = [self._style_map_trait_] * len(self._levels) # If the list of styles is shorter than the list of levels, # simply repeat styles from the beginning of the list as needed else: self._styles = [] for i in range(len(self._levels)): self._style_map_trait = self.styles[i%len(self.styles)] self._styles.append(self._style_map_trait_) self._styles_cache_valid = True #------------------------------------------------------------------------ # Event handlers #------------------------------------------------------------------------ def _widths_changed(self): if self._level_cache_valid: self._update_widths() self.invalidate_draw() def _styles_changed(self): if self._level_cache_valid: self._update_styles() self.invalidate_draw() def _negative_style_changed(self): if self._level_cache_valid: self._update_styles() self.invalidate_draw() def _positive_style_changed(self): if self._level_cache_valid: self._update_styles() self.invalidate_draw() chaco-4.5.0/chaco/contour_poly_plot.py0000644000076600000240000000666612426452312020615 0ustar jrocherstaff00000000000000""" Defines the ContourPolyPlot class. """ from __future__ import with_statement # Major library imports from numpy import array, linspace, meshgrid, transpose # Enthought library imports from traits.api import Bool, Dict # Local relative imports from base_contour_plot import BaseContourPlot from contour.contour import Cntr class ContourPolyPlot(BaseContourPlot): """ Contour image plot. Takes a value data object whose elements are scalars, and renders them as a contour plot. """ # TODO: Modify ImageData to explicitly support scalar value arrays #------------------------------------------------------------------------ # Private traits #------------------------------------------------------------------------ # Are the cached contours valid? If False, new ones need to be computed. _poly_cache_valid = Bool(False) # Cached collection of traces. _cached_polys = Dict #------------------------------------------------------------------------ # Private methods #------------------------------------------------------------------------ def _render(self, gc): """ Actually draws the plot. Implements the Base2DPlot interface. """ if not self._level_cache_valid: self._update_levels() if not self._poly_cache_valid: self._update_polys() if not self._colors_cache_valid: self._update_colors() with gc: gc.set_antialias(True) gc.clip_to_rect(self.x, self.y, self.width, self.height) gc.set_line_width(0) gc.set_alpha(self.alpha) for i in range(len(self._levels)-1): gc.set_fill_color(self._colors[i]) gc.set_stroke_color(self._colors[i]) key = (self._levels[i], self._levels[i+1]) for poly in self._cached_polys[key]: if self.orientation == "h": spoly = self.index_mapper.map_screen(poly) else: spoly = array(self.index_mapper.map_screen(poly))[:,::-1] gc.lines(spoly) gc.close_path() gc.draw_path() def _update_polys(self): """ Updates the cache of contour polygons """ # x and ydata are "fenceposts" so ignore the last value # XXX: this truncation is causing errors in Cntr() as of r13735 xdata = self.index._xdata.get_data() ydata = self.index._ydata.get_data() xs = linspace(xdata[0], xdata[-1], len(xdata)-1) ys = linspace(ydata[0], ydata[-1], len(ydata)-1) xg, yg = meshgrid(xs, ys) if self.orientation == "h": c = Cntr(xg, yg, self.value.raw_value) else: c = Cntr(xg, yg, self.value.raw_value.T) self._cached_contours = {} for i in range(len(self._levels)-1): key = (self._levels[i], self._levels[i+1]) self._cached_polys[key] = [] polys = c.trace(*key) for poly in polys: self._cached_polys[key].append(transpose(poly)) self._poly_cache_valid = True def _update_levels(self): """ Extends the parent method to also invalidate some other things """ super(ContourPolyPlot, self)._update_levels() self._poly_cache_valid = False def _update_colors(self): BaseContourPlot._update_colors(self, numcolors = len(self._levels) - 1) chaco-4.5.0/chaco/cross_plot_frame.py0000644000076600000240000001340412426452312020350 0ustar jrocherstaff00000000000000""" Defines the (deprecated) CrossPlotFrame class. """ ################################################################################# # # NOTE: PlotFrames are deprecated. There is no need to use them any more. # This class will be removed sometime in the near future. # ################################################################################# from __future__ import with_statement # Enthought library imports from traits.api import Bool, Float # Local, relative imports from base_plot_frame import BasePlotFrame from plot_containers import HPlotContainer, OverlayPlotContainer, VPlotContainer class CrossPlotFrame(BasePlotFrame): """ A simple, box-layout based plotframe. This class supports a central plot area with optional axes on the top, bottom, and sides. The legend can be placed to the bottom, left, right, or inside the plot area. The title can be placed along any of the four edges. NOTE: PlotFrames are deprecated. There is no need to use them any more. This class will be removed sometime in the future. """ # Slots or positions on the frame where plot components can place themselves. # Overrides PlotFrame. slot_names = ("center", "left", "right", "top", "bottom") # Default width and height. Class attribute. default_bounds = (500,500) # The sizes of the various areas # Width of the left slot. left_width = Float(50.0) # Width of the right slot. right_width = Float(50.0) # Height of the top slot. top_height = Float(50.0) # Height of the bottom slot. bottom_height = Float(50.0) # Does the component need to do a layout call? _layout_needed = Bool(True) def __init__(self, **kwtraits): if kwtraits.has_key("bounds"): bounds = kwtraits.pop("bounds") else: bounds = list(self.default_bounds) BasePlotFrame.__init__(self, **kwtraits) # Create our plot containers self.set_slot("center", OverlayPlotContainer(resizable="hv")) self.set_slot("left", HPlotContainer(resizable="v")) self.set_slot("right", HPlotContainer(resizable="v")) self.set_slot("top", VPlotContainer(resizable="h")) self.set_slot("bottom", VPlotContainer(resizable="h")) self.bounds = bounds return def set_visible_slots(self, *names): """ Convenience method to set the named slots to visible, while setting all others to not visible. """ for slot in self.slot_names: if slot in names: self.get_slot(slot).visible = True else: self.get_slot(slot).visible = False return #------------------------------------------------------------------------ # Protected methods #------------------------------------------------------------------------ def _draw_component(self, gc, view_bounds=None, mode="normal"): """ Draws the component. This method is preserved for backwards compatibility with _old_draw(). Overrides PlotComponent. """ with gc: gc.translate_ctm(*self.position) for slotname in self.slot_names: if getattr(self, slotname).visible: with gc: self.get_slot(slotname).draw(gc, view_bounds, mode) return def _do_layout(self): """ Performs a layout and sets the size and positions on this frame's containers, given its width and height. """ left = self.left right = self.right top = self.top bottom = self.bottom center = self.center # Calculate the bounds of the resizable center container, then set # the bounds on all the containers. center_x,_y represent the (x,y) # coordinate of the lower-left corner of the center region; # center_x2 and center_y2 represent the upper-right corner of the # center region. if self.left.visible: center_x = self.left_width else: center_x = self.x if self.bottom.visible: center_y = self.bottom_height else: center_y = self.y if self.right.visible: center_x2 = self.width - self.right_width - 1 else: center_x2 = self.width if self.top.visible: center_y2 = self.height - self.top_height - 1 else: center_y2 = self.height left.outer_position = [0.0, center_y] left.outer_bounds = [self.left_width, center_y2 - center_y + 1] right.outer_position = [center_x2 + 1, center_y] right.outer_bounds = [self.right_width, left.height] bottom.outer_position = [center_x, 0.0] bottom.outer_bounds = [center_x2 - center_x + 1, self.bottom_height] top.outer_position = [center_x, center_y2 + 1] top.outer_bounds = [bottom.width, self.top_height] center.outer_position = [center_x, center_y] center.outer_bounds = [bottom.width, left.height] for slot in self._frame_slots.values(): if slot.visible: preferred_size = slot.get_preferred_size() if "h" not in slot.resizable: slot.outer_width = preferred_size[0] if "v" not in slot.resizable: slot.outer_height = preferred_size[1] slot.do_layout() return ### Persistence ########################################################### #_pickles = ("left_width", "right_width", "top_height", "bottom_height") def __getstate__(self): state = super(CrossPlotFrame,self).__getstate__() for key in ['_layout_needed']: if state.has_key(key): del state[key] return state # EOF chaco-4.5.0/chaco/data_label.py0000644000076600000240000005026112426452312017061 0ustar jrocherstaff00000000000000""" Defines the DataLabel class and related trait and function. """ # Major library imports from math import sqrt from numpy import array, asarray, inf from numpy.linalg import norm # Enthought library imports from traits.api import Any, Array, Bool, Enum, Float, Int, List, \ Str, Tuple, Trait, on_trait_change, Property from enable.api import ColorTrait, MarkerTrait # Local, relative imports from scatterplot import render_markers from tooltip import ToolTip # Specifies the position of a label relative to its target. This can # be one of the text strings indicated, or a tuple or list of floats # representing the (x_offset, y_offset) in screen space of the label's # lower left corner. LabelPositionTrait = Trait("top right", Enum("bottom", "left", "right", "top", "top right", "top left", "bottom left", "bottom right"), Tuple, List) def draw_arrow(gc, pt1, pt2, color, arrowhead_size=10.0, offset1=0, offset2=0, arrow=None, minlen=0, maxlen=inf): """ Renders an arrow from *pt1* to *pt2*. If gc is None, then just returns the arrow object. Parameters ========== gc : graphics context where to render the arrow pt1 : point the origin of the arrow pt2 : point where the arrow is pointing color : a 3- or 4-tuple of color value the color to use for the arrow stem and head arrowhead_size : number screen units corresponding to the length of the arrowhead offset1 : number the amount of space from the start of the arrow to pt1 offset2 : number the amount of space from the tip of the arrow to pt2 arrow : object an opaque object returned by previous calls to draw_arrow. If this argument is provided, all other arguments (except gc) are ignored minlen: number or None the minimum length of the arrow; if the arrow is shorter than this, it will not be drawn maxlen: number or None the maximum length of the arrow; if the arrow is longer than this, it will not be drawn Returns ======= An 'arrow' (opaque object) which can be passed in to subsequent calls to this method to short-circuit some of the computation. Even if an arrow is not drawn (due to minlen/maxlen restrictions), an arrow will be returned. """ if arrow is None: pt1 = asarray(pt1) pt2 = asarray(pt2) unit_vec = pt2 - pt1 unit_vec /= norm(unit_vec) if unit_vec[0] == 0: perp_vec = array((0.3 * arrowhead_size, 0)) elif unit_vec[1] == 0: perp_vec = array((0, 0.3 * arrowhead_size)) else: slope = unit_vec[1] / unit_vec[0] perp_slope = -1 / slope perp_vec = array((1.0, perp_slope)) perp_vec *= 0.3 * arrowhead_size / norm(perp_vec) pt1 = pt1 + offset1 * unit_vec pt2 = pt2 - offset2 * unit_vec arrowhead_l = pt2 - (arrowhead_size * unit_vec + perp_vec) arrowhead_r = pt2 - (arrowhead_size * unit_vec - perp_vec) arrow = (pt1, pt2, arrowhead_l, arrowhead_r) else: pt1, pt2, arrowhead_l, arrowhead_r = arrow arrowlen = norm(pt2 - pt1) if arrowlen < minlen or arrowlen > maxlen: # This is the easiest way to circumvent the actual drawing gc = None if gc is not None: gc.set_stroke_color(color) gc.set_fill_color(color) gc.begin_path() gc.move_to(*pt1) gc.line_to(*pt2) gc.stroke_path() gc.move_to(*pt2) gc.line_to(*arrowhead_l) gc.line_to(*arrowhead_r) gc.fill_path() return arrow def find_region(px, py, x, y, x2, y2): """Classify the location of the point (px, py) relative to a rectangle. (x, y) and (x2, y2) are the lower-left and upper-right corners of the rectangle, respectively. (px, py) is classified as "left", "right", "top", "bottom" or "inside", according to the following diagram: \ top / \ / +----------+ left | inside | right +----------+ / \ / bottom \ """ if px < x: dx = x - px if py > y2 + dx: region = 'top' elif py < y - dx: region = 'bottom' else: region = 'left' elif px > x2: dx = px - x2 if py > y2 + dx: region = 'top' elif py < y - dx: region = 'bottom' else: region = 'right' else: # x <= px <= x2 if py > y2: region = 'top' elif py < y: region = 'bottom' else: region = 'inside' return region class DataLabel(ToolTip): """ A label on a point in data space. Optionally, an arrow is drawn to the point. """ # The symbol to use if **marker** is set to "custom". This attribute must # be a compiled path for the given Kiva context. custom_symbol = Any # The point in data space where this label should anchor itself. data_point = Trait(None, None, Tuple, List, Array) # The location of the data label relative to the data point. label_position = LabelPositionTrait # The format string that determines the label's text. This string is # formatted using a dict containing the keys 'x' and 'y', corresponding to # data space values. label_format = Str("(%(x)f, %(y)f)") # The text to show on the label, or above the coordinates for the label, if # show_label_coords is True label_text = Str # Flag whether to show coordinates with the label or not. show_label_coords = Bool(True) # Does the label clip itself against the main plot area? If not, then # the label draws into the padding area (where axes typically reside). clip_to_plot = Bool(True) # The center x position (average of x and x2) xmid = Property(Float, depends_on=['x', 'x2']) # The center y position (average of y and y2) ymid = Property(Float, depends_on=['y', 'y2']) # 'box' is a simple rectangular box, with an arrow that is a single line # with an arrowhead at the data point. # 'bubble' can be given rounded corners (by setting `corner_radius`), and # the 'arrow' is a thin triangular wedge with its point at the data point. # When label_style is 'bubble', the following traits are ignored: # arrow_size, arrow_color, arrow_root, and arrow_max_length. label_style = Enum('box', 'bubble') #---------------------------------------------------------------------- # Marker traits #---------------------------------------------------------------------- # Mark the point on the data that this label refers to? marker_visible = Bool(True) # The type of marker to use. This is a mapped trait using strings as the # keys. marker = MarkerTrait # The pixel size of the marker (doesn't include the thickness of the # outline). marker_size = Int(4) # The thickness, in pixels, of the outline to draw around the marker. # If this is 0, no outline will be drawn. marker_line_width = Float(1.0) # The color of the inside of the marker. marker_color = ColorTrait("red") # The color out of the border drawn around the marker. marker_line_color = ColorTrait("black") #---------------------------------------------------------------------- # Arrow traits #---------------------------------------------------------------------- # Draw an arrow from the label to the data point? Only # used if **data_point** is not None. arrow_visible = Bool(True) # FIXME: replace with some sort of ArrowStyle # The length of the arrowhead, in screen points (e.g., pixels). arrow_size = Float(10) # The color of the arrow. arrow_color = ColorTrait("black") # The position of the base of the arrow on the label. If this # is 'auto', then the label uses **label_position**. Otherwise, it # treats the label as if it were at the label position indicated by # this attribute. arrow_root = Trait("auto", "auto", "top left", "top right", "bottom left", "bottom right", "top center", "bottom center", "left center", "right center") # The minimum length of the arrow before it will be drawn. By default, # the arrow will be drawn regardless of how short it is. arrow_min_length = Float(0) # The maximum length of the arrow before it will be drawn. By default, # the arrow will be drawn regardless of how long it is. arrow_max_length = Float(inf) #---------------------------------------------------------------------- # Bubble traits #---------------------------------------------------------------------- # The radius (in screen coordinates) of the curved corners of the "bubble". corner_radius = Float(10) #------------------------------------------------------------------------- # Private traits #------------------------------------------------------------------------- # Tuple (sx, sy) of the mapped screen coordinates of **data_point**. _screen_coords = Any _cached_arrow = Any # When **arrow_root** is 'auto', this determines the location on the data # label from which the arrow is drawn, based on the position of the label # relative to its data point. _position_root_map = { "top left": "bottom right", "top right": "bottom left", "bottom left": "top right", "bottom right": "top left", "top center": "bottom center", "bottom center": "top center", "left center": "right center", "right center": "left center" } _root_positions = { "bottom right": ("x2", "y"), "bottom left": ("x", "y"), "top right": ("x2", "y2"), "top left": ("x", "y2"), "top center": ("xmid", "y2"), "bottom center": ("xmid", "y"), "left center": ("x", "ymid"), "right center": ("x2", "ymid"), } def overlay(self, component, gc, view_bounds=None, mode="normal"): """ Draws the tooltip overlaid on another component. Overrides and extends ToolTip.overlay() """ if self.clip_to_plot: gc.save_state() c = component gc.clip_to_rect(c.x, c.y, c.width, c.height) self.do_layout() if self.label_style == 'box': self._render_box(component, gc, view_bounds=view_bounds, mode=mode) else: self._render_bubble(component, gc, view_bounds=view_bounds, mode=mode) # draw the marker if self.marker_visible: render_markers(gc, [self._screen_coords], self.marker, self.marker_size, self.marker_color_, self.marker_line_width, self.marker_line_color_, self.custom_symbol) if self.clip_to_plot: gc.restore_state() def _render_box(self, component, gc, view_bounds=None, mode='normal'): # draw the arrow if necessary if self.arrow_visible: if self._cached_arrow is None: if self.arrow_root in self._root_positions: ox, oy = self._root_positions[self.arrow_root] else: if self.arrow_root == "auto": arrow_root = self.label_position else: arrow_root = self.arrow_root pos = self._position_root_map.get(arrow_root, "DUMMY") ox, oy = self._root_positions.get(pos, (self.x + self.width / 2, self.y + self.height / 2)) if type(ox) == str: ox = getattr(self, ox) oy = getattr(self, oy) self._cached_arrow = draw_arrow(gc, (ox, oy), self._screen_coords, self.arrow_color_, arrowhead_size=self.arrow_size, offset1=3, offset2=self.marker_size + 3, minlen=self.arrow_min_length, maxlen=self.arrow_max_length) else: draw_arrow(gc, None, None, self.arrow_color_, arrow=self._cached_arrow, minlen=self.arrow_min_length, maxlen=self.arrow_max_length) # layout and render the label itself ToolTip.overlay(self, component, gc, view_bounds, mode) def _render_bubble(self, component, gc, view_bounds=None, mode='normal'): """ Render the bubble label in the graphics context. """ # (px, py) is the data point in screen space. px, py = self._screen_coords # (x, y) is the lower left corner of the label. x = self.x y = self.y # (x2, y2) is the upper right corner of the label. x2 = self.x2 y2 = self.y2 # r is the corner radius. r = self.corner_radius if self.arrow_visible: # FIXME: Make 'gap_width' a configurable trait (and give it a # better name). max_gap_width = 10 gap_width = min(max_gap_width, abs(x2 - x - 2 * r), abs(y2 - y - 2 * r)) region = find_region(px, py, x, y, x2, y2) # Figure out where the "arrow" connects to the "bubble". if region == 'left' or region == 'right': gap_start = py - gap_width / 2 if gap_start < y + r: gap_start = y + r elif gap_start > y2 - r - gap_width: gap_start = y2 - r - gap_width by = gap_start + 0.5 * gap_width if region == 'left': bx = x else: bx = x2 else: gap_start = px - gap_width / 2 if gap_start < x + r: gap_start = x + r elif gap_start > x2 - r - gap_width: gap_start = x2 - r - gap_width bx = gap_start + 0.5 * gap_width if region == 'top': by = y2 else: by = y arrow_len = sqrt((px - bx) ** 2 + (py - by) ** 2) arrow_visible = (self.arrow_visible and (arrow_len >= self.arrow_min_length)) with gc: if self.border_visible: gc.set_line_width(self.border_width) gc.set_stroke_color(self.border_color_) else: gc.set_line_width(0) gc.set_stroke_color((0, 0, 0, 0)) gc.set_fill_color(self.bgcolor_) # Start at the lower left, on the left edge where the curved # part of the box ends. gc.move_to(x, y + r) # Draw the left side and the upper left curved corner. if arrow_visible and region == 'left': gc.line_to(x, gap_start) gc.line_to(px, py) gc.line_to(x, gap_start + gap_width) gc.arc_to(x, y2, x + r, y2, r) # Draw the top and the upper right curved corner. if arrow_visible and region == 'top': gc.line_to(gap_start, y2) gc.line_to(px, py) gc.line_to(gap_start + gap_width, y2) gc.arc_to(x2, y2, x2, y2 - r, r) # Draw the right side and the lower right curved corner. if arrow_visible and region == 'right': gc.line_to(x2, gap_start + gap_width) gc.line_to(px, py) gc.line_to(x2, gap_start) gc.arc_to(x2, y, x2 - r, y, r) # Draw the bottom and the lower left curved corner. if arrow_visible and region == 'bottom': gc.line_to(gap_start + gap_width, y) gc.line_to(px, py) gc.line_to(gap_start, y) gc.arc_to(x, y, x, y + r, r) # Finish the "bubble". gc.draw_path() self._draw_overlay(gc) def _do_layout(self, size=None): """Computes the size and position of the label and arrow. Overrides and extends ToolTip._do_layout() """ if not self.component or not hasattr(self.component, "map_screen"): return # Call the parent class layout. This computes all the label ToolTip._do_layout(self) self._screen_coords = self.component.map_screen([self.data_point])[0] sx, sy = self._screen_coords if isinstance(self.label_position, str): orientation = self.label_position if ("left" in orientation) or ("right" in orientation): if " " not in orientation: self.y = sy - self.height / 2 if "left" in orientation: self.outer_x = sx - self.outer_width - 1 elif "right" in orientation: self.outer_x = sx if ("top" in orientation) or ("bottom" in orientation): if " " not in orientation: self.x = sx - self.width / 2 if "bottom" in orientation: self.outer_y = sy - self.outer_height - 1 elif "top" in orientation: self.outer_y = sy if "center" in orientation: if " " not in orientation: self.x = sx - (self.width / 2) self.y = sy - (self.height / 2) else: self.x = sx - (self.outer_width / 2) - 1 self.y = sy - (self.outer_height / 2) - 1 else: self.x = sx + self.label_position[0] self.y = sy + self.label_position[1] self._cached_arrow = None return def _data_point_changed(self, old, new): if new is not None: self._create_new_labels() def _label_format_changed(self, old, new): self._create_new_labels() def _label_text_changed(self, old, new): self._create_new_labels() def _show_label_coords_changed(self, old, new): self._create_new_labels() def _create_new_labels(self): pt = self.data_point if pt is not None: if self.show_label_coords: self.lines = [self.label_text, self.label_format % {"x": pt[0], "y": pt[1]}] else: self.lines = [self.label_text] def _component_changed(self, old, new): for comp, attach in ((old, False), (new, True)): if comp is not None: if hasattr(comp, 'index_mapper'): self._modify_mapper_listeners(comp.index_mapper, attach=attach) if hasattr(comp, 'value_mapper'): self._modify_mapper_listeners(comp.value_mapper, attach=attach) return def _modify_mapper_listeners(self, mapper, attach=True): if mapper is not None: mapper.on_trait_change(self._handle_mapper, 'updated', remove=not attach) return def _handle_mapper(self): # This gets fired whenever a mapper on our plot fires its # 'updated' event. self._layout_needed = True @on_trait_change("arrow_size,arrow_root,arrow_min_length," + "arrow_max_length") def _invalidate_arrow(self): self._cached_arrow = None self._layout_needed = True @on_trait_change("label_position,position,position_items,bounds," + "bounds_items") def _invalidate_layout(self): self._layout_needed = True def _get_xmid(self): return 0.5 * (self.x + self.x2) def _get_ymid(self): return 0.5 * (self.y + self.y2) chaco-4.5.0/chaco/data_range_1d.py0000644000076600000240000004542312426452312017466 0ustar jrocherstaff00000000000000""" Defines the DataRange1D class. """ # Major library imports from math import ceil, floor, log from numpy import compress, inf, isinf, isnan, ndarray # Enthought library imports from traits.api import Bool, CFloat, Enum, Float, Property, Trait, \ Callable # Local relative imports from base import arg_find_runs from base_data_range import BaseDataRange from ticks import heckbert_interval class DataRange1D(BaseDataRange): """ Represents a 1-D data range. """ # The actual value of the lower bound of this range (overrides # AbstractDataRange). To set it, use **low_setting**. low = Property # The actual value of the upper bound of this range (overrides # AbstractDataRange). To set it, use **high_setting**. high = Property # Property for the lower bound of this range (overrides AbstractDataRange). # # * 'auto': The lower bound is automatically set at or below the minimum # of the data. # * 'track': The lower bound tracks the upper bound by **tracking_amount**. # * CFloat: An explicit value for the lower bound low_setting = Property(Trait('auto', 'auto', 'track', CFloat)) # Property for the upper bound of this range (overrides AbstractDataRange). # # * 'auto': The upper bound is automatically set at or above the maximum # of the data. # * 'track': The upper bound tracks the lower bound by **tracking_amount**. # * CFloat: An explicit value for the upper bound high_setting = Property(Trait('auto', 'auto', 'track', CFloat)) # Do "auto" bounds imply an exact fit to the data? If False, # they pad a little bit of margin on either side. tight_bounds = Bool(True) # A user supplied function returning the proper bounding interval. # bounds_func takes (data_low, data_high, margin, tight_bounds) # and returns (low, high) bounds_func = Callable # The amount of margin to place on either side of the data, expressed as # a percentage of the full data width margin = Float(0.05) # The minimum percentage difference between low and high. That is, # (high-low) >= epsilon * low. # Used to be 1.0e-20 but chaco cannot plot at such a precision! epsilon = CFloat(1.0e-10) # When either **high** or **low** tracks the other, track by this amount. default_tracking_amount = CFloat(20.0) # The current tracking amount. This value changes with zooming. tracking_amount = default_tracking_amount # Default tracking state. This value is used when self.reset() is called. # # * 'auto': Both bounds reset to 'auto'. # * 'high_track': The high bound resets to 'track', and the low bound # resets to 'auto'. # * 'low_track': The low bound resets to 'track', and the high bound # resets to 'auto'. default_state = Enum('auto', 'high_track', 'low_track') # FIXME: this attribute is not used anywhere, is it safe to remove it? # Is this range dependent upon another range? fit_to_subset = Bool(False) #------------------------------------------------------------------------ # Private traits #------------------------------------------------------------------------ # The "_setting" attributes correspond to what the user has "set"; the # "_value" attributes are the actual numerical values for the given # setting. # The user-specified low setting. _low_setting = Trait('auto', 'auto', 'track', CFloat) # The actual numerical value for the low setting. _low_value = CFloat(-inf) # The user-specified high setting. _high_setting = Trait('auto', 'auto', 'track', CFloat) # The actual numerical value for the high setting. _high_value = CFloat(inf) # A list of attributes to persist # _pickle_attribs = ("_low_setting", "_high_setting") #------------------------------------------------------------------------ # AbstractRange interface #------------------------------------------------------------------------ def clip_data(self, data): """ Returns a list of data values that are within the range. Implements AbstractDataRange. """ return compress(self.mask_data(data), data) def mask_data(self, data): """ Returns a mask array, indicating whether values in the given array are inside the range. Implements AbstractDataRange. """ return ((data.view(ndarray) >= self._low_value) & (data.view(ndarray) <= self._high_value)) def bound_data(self, data): """ Returns a tuple of indices for the start and end of the first run of *data* that falls within the range. Implements AbstractDataRange. """ mask = self.mask_data(data) runs = arg_find_runs(mask, "flat") # Since runs of "0" are also considered runs, we have to cycle through # until we find the first run of "1"s. for run in runs: if mask[run[0]] == 1: # arg_find_runs returns 1 past the end return run[0], run[1] - 1 return (0, 0) def set_bounds(self, low, high): """ Sets all the bounds of the range simultaneously. Implements AbstractDataRange. """ if low == 'track': # Set the high setting first result_high = self._do_set_high_setting(high, fire_event=False) result_low = self._do_set_low_setting(low, fire_event=False) result = result_low or result_high else: # Either set low first or order doesn't matter result_low = self._do_set_low_setting(low, fire_event=False) result_high = self._do_set_high_setting(high, fire_event=False) result = result_high or result_low if result: self.updated = result def scale_tracking_amount(self, multiplier): """ Sets the **tracking_amount** to a new value, scaled by *multiplier*. """ self.tracking_amount = self.tracking_amount * multiplier self._do_track() def set_tracking_amount(self, amount): """ Sets the **tracking_amount** to a new value, *amount*. """ self.tracking_amount = amount self._do_track() def set_default_tracking_amount(self, amount): """ Sets the **default_tracking_amount** to a new value, *amount*. """ self.default_tracking_amount = amount #------------------------------------------------------------------------ # Public methods #------------------------------------------------------------------------ def reset(self): """ Resets the bounds of this range, based on **default_state**. """ # need to maintain 'track' setting if self.default_state == 'auto': self._high_setting = 'auto' self._low_setting = 'auto' elif self.default_state == 'low_track': self._high_setting = 'auto' self._low_setting = 'track' elif self.default_state == 'high_track': self._high_setting = 'track' self._low_setting = 'auto' self._refresh_bounds() self.tracking_amount = self.default_tracking_amount def refresh(self): """ If any of the bounds is 'auto', this method refreshes the actual low and high values from the set of the view filters' data sources. """ if ('auto' in (self._low_setting, self._high_setting)) or \ ('track' in (self._low_setting, self._high_setting)): # If the user has hard-coded bounds, then refresh() doesn't do # anything. self._refresh_bounds() else: return #------------------------------------------------------------------------ # Private methods (getters and setters) #------------------------------------------------------------------------ def _get_low(self): return float(self._low_value) def _set_low(self, val): return self._set_low_setting(val) def _get_low_setting(self): return self._low_setting def _do_set_low_setting(self, val, fire_event=True): """ Returns ------- If fire_event is False and the change would have fired an event, returns the tuple of the new low and high values. Otherwise returns None. In particular, if fire_event is True, it always returns None. """ new_values = None if self._low_setting != val: # Save the new setting. self._low_setting = val # If val is 'auto' or 'track', get the corresponding numerical # value. if val == 'auto': if len(self.sources) > 0: val = min([source.get_bounds()[0] for source in self.sources]) else: val = -inf elif val == 'track': if len(self.sources) > 0 or self._high_setting != 'auto': val = self._high_value - self.tracking_amount else: val = -inf # val is now a numerical value. If it is the same as the current # value, there is nothing to do. if self._low_value != val: self._low_value = val if self._high_setting == 'track': self._high_value = val + self.tracking_amount if fire_event: self.updated = (self._low_value, self._high_value) else: new_values = (self._low_value, self._high_value) return new_values def _set_low_setting(self, val): self._do_set_low_setting(val, True) def _get_high(self): return float(self._high_value) def _set_high(self, val): return self._set_high_setting(val) def _get_high_setting(self): return self._high_setting def _do_set_high_setting(self, val, fire_event=True): """ Returns ------- If fire_event is False and the change would have fired an event, returns the tuple of the new low and high values. Otherwise returns None. In particular, if fire_event is True, it always returns None. """ new_values = None if self._high_setting != val: # Save the new setting. self._high_setting = val # If val is 'auto' or 'track', get the corresponding numerical # value. if val == 'auto': if len(self.sources) > 0: val = max([source.get_bounds()[1] for source in self.sources]) else: val = inf elif val == 'track': if len(self.sources) > 0 or self._low_setting != 'auto': val = self._low_value + self.tracking_amount else: val = inf # val is now a numerical value. If it is the same as the current # value, there is nothing to do. if self._high_value != val: self._high_value = val if self._low_setting == 'track': self._low_value = val - self.tracking_amount if fire_event: self.updated = (self._low_value, self._high_value) else: new_values = (self._low_value, self._high_value) return new_values def _set_high_setting(self, val): self._do_set_high_setting(val, True) def _refresh_bounds(self): null_bounds = False if len(self.sources) == 0: null_bounds = True else: bounds_list = [source.get_bounds() for source in self.sources \ if source.get_size() > 0] if len(bounds_list) == 0: null_bounds = True if null_bounds: # If we have no sources and our settings are "auto", then reset our # bounds to infinity; otherwise, set the _value to the corresponding # setting. if (self._low_setting in ("auto", "track")): self._low_value = -inf else: self._low_value = self._low_setting if (self._high_setting in ("auto", "track")): self._high_value = inf else: self._high_value = self._high_setting return else: mins, maxes = zip(*bounds_list) low_start, high_start = \ calc_bounds(self._low_setting, self._high_setting, mins, maxes, self.epsilon, self.tight_bounds, margin=self.margin, track_amount=self.tracking_amount, bounds_func=self.bounds_func) if (self._low_value != low_start) or (self._high_value != high_start): self._low_value = low_start self._high_value = high_start self.updated = (self._low_value, self._high_value) return def _do_track(self): changed = False if self._low_setting == 'track': new_value = self._high_value - self.tracking_amount if self._low_value != new_value: self._low_value = new_value changed = True elif self._high_setting == 'track': new_value = self._low_value + self.tracking_amount if self._high_value != new_value: self._high_value = new_value changed = True if changed: self.updated = (self._low_value, self._high_value) #------------------------------------------------------------------------ # Event handlers #------------------------------------------------------------------------ def _sources_items_changed(self, event): self.refresh() for source in event.removed: source.on_trait_change(self.refresh, "data_changed", remove=True) for source in event.added: source.on_trait_change(self.refresh, "data_changed") def _sources_changed(self, old, new): self.refresh() for source in old: source.on_trait_change(self.refresh, "data_changed", remove=True) for source in new: source.on_trait_change(self.refresh, "data_changed") #------------------------------------------------------------------------ # Serialization interface #------------------------------------------------------------------------ def _post_load(self): self._sources_changed(None, self.sources) ###### method to calculate bounds for a given 1-dimensional set of data def calc_bounds(low_set, high_set, mins, maxes, epsilon, tight_bounds, margin=0.08, track_amount=0, bounds_func=None): """ Calculates bounds for a given 1-D set of data. Parameters ---------- low_set : 'auto', 'track', or number Current low setting high_set : 'auto', 'track', or number Current high setting mins : list of numbers Potential minima. maxes : list Potential maxima. epsilon : number Minimum percentage difference between bounds tight_bounds : Boolean Do 'auto' bounds imply an exact fit to the data? If False, they pad a little bit of margin on either side. margin : float (default=0.08) The margin, expressed as a percentage of total data width, to place on either side of the data if tight_bounds is False. track_amount : number The amount by which a 'track' bound tracks another bound. bounds_func : Callable A callable which can override the bounds calculation. Returns ------- (min, max) for the new bounds. If either of the calculated bounds is NaN, returns (0,0). Description ----------- Setting both *low_set* and *high_set* to 'track' is an invalid state; the method copes by setting *high_set* to 'auto', and proceeding. """ if (low_set == 'track') and (high_set == 'track'): high_set = 'auto' if low_set == 'auto': real_min = min(mins) elif low_set == 'track': # real_max hasn't been set yet pass else: real_min = low_set if high_set == 'auto': real_max = max(maxes) elif high_set == 'track': # real_min has been set now real_max = real_min + track_amount else: real_max = high_set # Go back and set real_min if we need to if low_set == 'track': real_min = real_max - track_amount # If we're all NaNs, just return a 0,1 range if isnan(real_max) or isnan(real_min): return 0, 0 if not isinf(real_min) and not isinf(real_max) and \ (abs(real_max - real_min) <= abs(epsilon * real_min)): # If we get here, then real_min and real_max are (for all # intents and purposes) identical, and so we just base # everything off of real_min. # Note: we have to use <= and not strict < because otherwise # we won't catch the cases when real_min == 0.0. if abs(real_min) > 1.0: # Round up to the next power of ten that encloses these log_val = log(abs(real_min), 10) if real_min >= 0: real_min = pow(10, floor(log_val)) real_max = pow(10, ceil(log_val)) else: real_min = -pow(10, ceil(log_val)) real_max = -pow(10, floor(log_val)) else: # If the user has a constant value less than 1, then these # are the bounds we use. if real_min > 0.0: real_max = 2 * real_min real_min = 0.0 elif real_min == 0.0: real_min = -1.0 real_max = 1.0 else: real_min = 2 * real_min real_max = 0.0 # Now test if the bounds leave some room around the data, unless # tight_bounds==True or unless another function to compute the bound # is provided. if bounds_func is not None: return bounds_func(real_min, real_max, margin, tight_bounds) elif not tight_bounds: low, high, d = heckbert_interval(real_min, real_max) # 2nd run of heckbert_interval necessary? Will be if bounds are # too tights (ie within the margin). rerun = False if abs(low - real_min) / (high - low) < margin: modified_min = real_min - (high - low) * margin rerun = True else: modified_min = real_min if abs(high - real_max) / (high - low) < margin: modified_max = real_max + (high - low) * margin rerun = True else: modified_max = real_max if rerun: low, high, d = heckbert_interval(modified_min, modified_max) return low, high else: return real_min, real_max chaco-4.5.0/chaco/data_range_2d.py0000644000076600000240000002040312426462254017464 0ustar jrocherstaff00000000000000""" Defines the DataRange2D class. """ from numpy import compress, inf, transpose # Enthought library imports from traits.api import Any, Bool, CFloat, Instance, Property, Trait, \ Tuple, on_trait_change # Local relative imports from base_data_range import BaseDataRange from data_range_1d import DataRange1D class DataRange2D(BaseDataRange): """ A range on (2-D) image data. In a mathematically general sense, a 2-D range is an arbitrary region in the plane. Arbitrary regions are difficult to implement well, so this class supports only rectangular regions for now. """ # The actual value of the lower bound of this range. To set it, use # **low_setting**. low = Property # (2,) array of lower-left x,y # The actual value of the upper bound of this range. To set it, use # **high_setting**. high = Property # (2,) array of upper-right x,y # Property for the lower bound of this range (overrides AbstractDataRange). low_setting = Property # Property for the upper bound of this range (overrides AbstractDataRange). high_setting = Property # The 2-D grid range is actually implemented as two 1-D ranges, which can # be accessed individually. They can also be set to new DataRange1D # instances; in that case, the DataRange2D's sources are removed from # its old 1-D dataranges and added to the new one. # Property for the range in the x-dimension. x_range = Property # Property for the range in the y-dimension. y_range = Property # Do "auto" bounds imply an exact fit to the data? (One Boolean per # dimension) If False, the bounds pad a little bit of margin on either # side. tight_bounds = Tuple(Bool(True), Bool(True)) # The minimum percentage difference between low and high for each # dimension. That is, (high-low) >= epsilon * low. epsilon = Tuple(CFloat(1.0e-4), CFloat(1.0e-4)) #------------------------------------------------------------------------ # Private traits #------------------------------------------------------------------------ # DataRange1D for the x-dimension. _xrange = Instance(DataRange1D, args=()) # DataRange1D for the y-dimension. _yrange = Instance(DataRange1D, args=()) #------------------------------------------------------------------------ # AbstractRange interface #------------------------------------------------------------------------ def clip_data(self, data): """ Returns a list of data values that are within the range. Implements AbstractDataRange. """ return compress(self.mask_data(data), data, axis=0) def mask_data(self, data): """ Returns a mask array, indicating whether values in the given array are inside the range. Implements AbstractDataRange. """ x_points, y_points = transpose(data) x_mask = (x_points >= self.low[0]) & (x_points <= self.high[0]) y_mask = (y_points >= self.low[1]) & (y_points <= self.high[1]) return x_mask & y_mask def bound_data(self, data): """ Not implemented for this class. """ raise NotImplementedError("bound_data() has not been implemented " "for 2d pointsets.") def set_bounds(self, low, high): """ Sets all the bounds of the range simultaneously. Implements AbstractDataRange. Parameters ---------- low : (x,y) Lower-left corner of the range. high : (x,y) Upper right corner of the range. """ self._do_set_low_setting(low, fire_event=False) self._do_set_high_setting(high) #------------------------------------------------------------------------ # Public methods #------------------------------------------------------------------------ def __init__(self, *args, **kwargs): super(DataRange2D, self).__init__(*args, **kwargs) def reset(self): """ Resets the bounds of this range. """ self.high_setting = ('auto', 'auto') self.low_setting = ('auto', 'auto') self.refresh() def refresh(self): """ If any of the bounds is 'auto', this method refreshes the actual low and high values from the set of the view filters' data sources. """ if 'auto' not in self.low_setting and \ 'auto' not in self.high_setting: # If the user has hard-coded bounds, then refresh() doesn't do # anything. return else: self._refresh_bounds() #------------------------------------------------------------------------ # Private methods #------------------------------------------------------------------------ def _refresh_bounds(self): self._xrange.refresh() self._yrange.refresh() #------------------------------------------------------------------------ # Property getters and setters #------------------------------------------------------------------------ def _get_low(self): return (self._xrange.low, self._yrange.low) def _set_low(self, val): return self._set_low_setting(val) def _get_low_setting(self): return (self._xrange.low_setting, self._yrange.low_setting) def _set_low_setting(self, val): self._do_set_low_setting(val) def _do_set_low_setting(self, val, fire_event=True): self._xrange.low_setting = val[0] self._yrange.low_setting = val[1] def _get_high(self): return (self._xrange.high, self._yrange.high) def _set_high(self, val): return self._set_high_setting(val) def _get_high_setting(self): return (self._xrange.high_setting, self._yrange.high_setting) def _set_high_setting(self, val): self._do_set_high_setting(val) def _do_set_high_setting(self, val, fire_event=True): self._xrange.high_setting = val[0] self._yrange.high_setting = val[1] def _get_x_range(self): return self._xrange def _set_x_range(self, newrange): self._set_1d_range("_xdata", self._xrange, newrange) self._xrange = newrange def _get_y_range(self): return self._yrange def _set_y_range(self, newrange): self._set_1d_range("_ydata", self._yrange, newrange) self._yrange = newrange def _set_1d_range(self, dataname, oldrange, newrange): # dataname is the name of the underlying 1d data source of the # ImageData instances in self.sources, e.g. "_xdata" or "_ydata" for source in self.sources: source1d = getattr(source, dataname, None) if source1d: if oldrange: oldrange.remove(source1d) if newrange: newrange.add(source1d) return #------------------------------------------------------------------------ # Event handlers #------------------------------------------------------------------------ def _sources_items_changed(self, event): for source in event.removed: source.on_trait_change(self.refresh, "data_changed", remove=True) for source in event.added: source.on_trait_change(self.refresh, "data_changed") # the _xdata and _ydata of the sources may be created anew on every # access, so we can't just add/delete from _xrange and _yrange sources # based on object identity. So recreate lists each time: self._xrange.sources = [s._xdata for s in self.sources] self._yrange.sources = [s._ydata for s in self.sources] self.refresh() def _sources_changed(self, old, new): for source in old: source.on_trait_change(self.refresh, "data_changed", remove=True) for source in new: source.on_trait_change(self.refresh, "data_changed") # the _xdata and _ydata of the sources may be created anew on every # access, so we can't just add/delete from _xrange and _yrange sources # based on object identity. So recreate lists each time: self._xrange.sources = [s._xdata for s in self.sources] self._yrange.sources = [s._ydata for s in self.sources] self.refresh() @on_trait_change("_xrange.updated,_yrange.updated") def _subranges_updated(self): self.updated = True chaco-4.5.0/chaco/data_view.py0000644000076600000240000004116612426452312016760 0ustar jrocherstaff00000000000000""" Defines the DataView class, and associated property traits and property functions. """ from numpy import array, transpose from traits.api import Bool, Enum, Instance, Property from enable.colors import color_table from abstract_overlay import AbstractOverlay from axis import PlotAxis from base_1d_mapper import Base1DMapper from base_2d_plot import Base2DPlot from data_range_2d import DataRange2D from grid import PlotGrid from linear_mapper import LinearMapper from log_mapper import LogMapper from plot_containers import OverlayPlotContainer #----------------------------------------------------------------------------- # Define new traits to condense the definition of some convenience # properties in the Plot class #----------------------------------------------------------------------------- def get_mapper(self, attr_name): """ Getter function used by OrientedMapperProperty. """ if (attr_name,self.orientation) in [("x_mapper","h"), ("y_mapper","v")]: return self.index_mapper else: return self.value_mapper def set_mapper(self, attr_name, new): """ Setter function used by OrientedMapperProperty. """ if (attr_name,self.orientation) in [("x_mapper","h"), ("y_mapper","v")]: self.index_mapper = new else: self.value_mapper = new # Property that represents a mapper for an orientation. OrientedMapperProperty = Property(get_mapper, set_mapper) def get_axis(self, attr_name): """ Getter function used by AxisProperty. """ if (attr_name,self.orientation) in [("index_axis","h"), ("value_axis","v")]: return self.x_axis else: return self.y_axis def set_axis(self, attr_name, new): """ Setter function used by AxisProperty. """ if (attr_name,self.orientation) in [("index_axis","h"), ("value_axis","v")]: self.x_axis = new else: self.y_axis = new # Property that represents an axis. AxisProperty = Property(get_axis, set_axis) def get_grid(self, attr_name): """ Getter function used by GridProperty. """ if (attr_name,self.orientation) in [("index_grid","v"), ("value_grid","h")]: return self.y_grid else: return self.x_grid def set_grid(self, attr_name, new): """ Setter function used by GridProperty. """ if (attr_name,self.orientation) in [("index_grid","v"), ("value_grid","h")]: self.y_grid = new else: self.y_grid = new # Property that represents a grid for a particular orientation. GridProperty = Property(get_grid, set_grid) class DataView(OverlayPlotContainer): """ Represents a mapping from 2-D data space into 2-D screen space. It can house renderers and other plot components, and otherwise behaves just like a normal PlotContainer. """ # The orientation of the index axis. orientation = Enum("h", "v") # The default location of the origin for new plots default_origin = Enum("bottom left", "top left", "bottom right", "top right") # The origin reported to axes, etc origin = Property(depends_on='default_origin') # Whether our map_screen and map_data should treat screen-space # coords as being in our coordinate space or in our contained # coordinate space. # The mapper to use for the index data. index_mapper = Instance(Base1DMapper) # The mapper to use for value data. value_mapper = Instance(Base1DMapper) # For x-y plots, the scale of the index axis. index_scale = Enum("linear", "log") # For x-y plots, the scale of the index axis. value_scale = Enum("linear", "log") # The range used for the index data. index_range = Property # The range used for the value data. value_range = Property # The 2-D data range whose x- and y-ranges are exposed as the # **index_range** and **value_range** property traits. This allows # supporting both XY plots and 2-D (image) plots. range2d = Instance(DataRange2D) # Convenience property that offers access to whatever mapper corresponds # to the X-axis. x_mapper = OrientedMapperProperty # Convenience property that offers access to whatever mapper corresponds # to the Y-axis y_mapper = OrientedMapperProperty #------------------------------------------------------------------------ # Axis and Grids #------------------------------------------------------------------------ # The horizontal axis. Its position relative to the plot # area can be "top", "bottom", or "float". The default position for a new # x-axis is "bottom". # # TODO: For now, this is an instance of AbstractOverlay instead of PlotAxis # because scales_axis.PlotAxis doesn't inherit from PlotAxis, but instead is a # semi-reimplementation. Thus, rather than making scales_axis.PlotAxis # inherit a concrete class, I chose to loosen this trait by specifying # a more general base class of PlotAxis. This incurs lower risk of subtle # and difficult-to-catch bugs being introduced by changes to the # axis.PlotAxis class. This same comment applies to the y_axis trait # below. --pwang #x_axis = Instance(PlotAxis) x_axis = Instance(AbstractOverlay) # The vertical axis. Its position relative to the plot # area can be "left", "right", or "float". The default position for a new # y-axis is "left". #y_axis = Instance(PlotAxis) y_axis = Instance(AbstractOverlay) # The grid that intersects the x-axis, i.e., a set of vertical lines. x_grid = Instance(PlotGrid) # The grid that intersects the y-axis, i.e., a set of horizontal lines. y_grid = Instance(PlotGrid) # Whether to automatically create the x_axis and y_axis if they were not # already set by the caller. auto_axis = Bool(True) # Whether to automatically create the x_grid and y_grid if they were not # already set by the caller. auto_grid = Bool(True) # Convenience property for accessing the index axis, which can be X or Y, # depending on **orientation**. index_axis = AxisProperty # Convenience property for accessing the value axis, which can be Y or X, # depending on **orientation**. value_axis = AxisProperty # Convenience property for accessing the index grid, which can be horizontal # or vertical, depending on **orientation**. index_grid = GridProperty # Convenience property for accessing the value grid, which can be vertical # or horizontal, depending on **orientation**. value_grid = GridProperty #------------------------------------------------------------------------ # Appearance #------------------------------------------------------------------------ # Background color (overrides Enable Component) bgcolor = "white" # Padding defaults. padding_top = 50 padding_bottom = 50 padding_left = 50 padding_right = 50 border_visible = True #------------------------------------------------------------------------ # Public methods #------------------------------------------------------------------------ def __init__(self, **kwtraits): super(DataView, self).__init__(**kwtraits) self._init_components() # If we are not resizable, we will not get a bounds update upon layout, # so we have to manually update our mappers if self.resizable == "": self._update_mappers() return def map_screen(self, data_array): """ Maps an array of data points to screen space and returns an array of screen space points. """ # data_array is Nx2 array if len(data_array) == 0: return [] x_ary, y_ary = transpose(data_array) sx = self.index_mapper.map_screen(x_ary) sy = self.value_mapper.map_screen(y_ary) if self.orientation == "h": return transpose(array((sx,sy))) else: return transpose(array((sy,sx))) def map_data(self, screen_pt): """ Maps a screen space point into the 2D data space of this plot. (Note that this differs from the BaseXYPlot implementation, which by default only maps into the 1D index space.) """ # At some point it would be good to change the DataView to use # the GridMapper, and then use its map_data() method. x, y = screen_pt return array((self.index_mapper.map_data(x), self.value_mapper.map_data(y))) #------------------------------------------------------------------------ # Private methods #------------------------------------------------------------------------ def _init_components(self): # Since this is called after the HasTraits constructor, we have to make # sure that we don't blow away any components that the caller may have # already set. if not self.range2d: self.range2d = DataRange2D() if not self.index_mapper: if self.index_scale == "linear": imap = LinearMapper(range=self.range2d.x_range) else: imap = LogMapper(range=self.range2d.x_range) self.index_mapper = imap if not self.value_mapper: if self.value_scale == "linear": vmap = LinearMapper(range=self.range2d.y_range) else: vmap = LogMapper(range=self.range2d.y_range) self.value_mapper = vmap # make sure the grid and bgcolor are not the same color grid_color = 'lightgray' if color_table[self.bgcolor] == color_table[grid_color]: grid_color = 'white' if not self.x_grid and self.auto_grid: self.x_grid = PlotGrid(mapper=self.x_mapper, orientation="vertical", line_color=grid_color, line_style="dot", component=self) if not self.y_grid and self.auto_grid: self.y_grid = PlotGrid(mapper=self.y_mapper, orientation="horizontal", line_color=grid_color, line_style="dot", component=self) if not self.x_axis and self.auto_axis: self.x_axis = PlotAxis(mapper=self.x_mapper, orientation="bottom", component=self) if not self.y_axis and self.auto_axis: self.y_axis = PlotAxis(mapper=self.y_mapper, orientation="left", component=self) #------------------------------------------------------------------------- # Event handlers #------------------------------------------------------------------------- def _update_mappers(self): x = self.x x2 = self.x2 y = self.y y2 = self.y2 if self.x_mapper is not None: if "left" in self.origin: self.x_mapper.low_pos = x self.x_mapper.high_pos = x2 else: self.x_mapper.low_pos = x2 self.x_mapper.high_pos = x if self.y_mapper is not None: if "bottom" in self.origin: self.y_mapper.low_pos = y self.y_mapper.high_pos = y2 else: self.y_mapper.low_pos = y2 self.y_mapper.high_pos = y self.invalidate_draw() return def _bounds_changed(self, old, new): super(DataView, self)._bounds_changed(old, new) self._update_mappers() def _bounds_items_changed(self, event): super(DataView, self)._bounds_items_changed(event) self._update_mappers() def _position_changed(self, old, new): super(DataView, self)._position_changed(old, new) self._update_mappers() def _position_items_changed(self, event): super(DataView, self)._position_items_changed(event) self._update_mappers() def _origin_changed(self): self._update_mappers() def _orientation_changed(self): # Change our grid and axis mappers so that they correspond to the # new mapper configuration. Using the self.x_mapper and self.y_mapper # properties will grab the correct mappers corresponding to our new # orientation. if self.x_grid is not None: self.x_grid.mapper = self.x_mapper if self.y_grid is not None: self.y_grid.mapper = self.y_mapper if self.x_axis is not None: self.x_axis.mapper = self.x_mapper if self.y_axis is not None: self.y_axis.mapper = self.y_mapper self._update_mappers() for renderer in self.components: if hasattr(renderer, "orientation"): renderer.orientation = self.orientation return def _index_mapper_changed(self, old, new): if new is not None: if new.range is not None: # Add the range's datasources to our range for source in new.range.sources: self.index_range.add(source) new.range = self.index_range if self.index_axis: self.index_axis.mapper = new if self.index_grid: self.index_grid.mapper = new def _value_mapper_changed(self, old, new): if new is not None: if new.range is not None: # Add the range's datasources to our range for source in new.range.sources: self.value_range.add(source) new.range = self.value_range if self.value_axis: self.value_axis.mapper = new if self.value_grid: self.value_grid.mapper = new def _bgcolor_changed(self): self.invalidate_draw() def _x_grid_changed(self, old, new): self._underlay_change_helper(old, new) def _y_grid_changed(self, old, new): self._underlay_change_helper(old, new) def _x_axis_changed(self, old, new): self._underlay_change_helper(old, new) def _y_axis_changed(self, old, new): self._underlay_change_helper(old, new) def _underlay_change_helper(self, old, new): if old in self.underlays: self.underlays.remove(old) if new is not None: self.underlays.append(new) def _overlay_change_helper(self, old, new): if old in self.overlays: self.overlays.remove(old) if new is not None: self.overlays.append(new) def _range2d_changed(self, old, new): if new is not None: if self.index_mapper is not None: self.index_mapper.range = new.x_range if self.value_mapper is not None: self.value_mapper.range = new.y_range else: self.index_mapper.range = None self.value_mapper.range = None if old is not None: for datasource in old.sources[:]: old.remove(datasource) if new is not None: new.add(datasource) for renderer in self.components: if hasattr(renderer, 'range2d'): renderer.range2d = new elif isinstance(renderer, Base2DPlot): renderer.index_range = new else: if hasattr(renderer, 'index_range'): setattr(renderer, 'index_range', self.index_range) if hasattr(renderer, 'value_range'): setattr(renderer, 'value_range', self.value_range) return def _range2d_default(self): """ Default trait initializer for the range2d trait """ return DataRange2D() #------------------------------------------------------------------------ # Property getters and setters #------------------------------------------------------------------------ def _get_index_range(self): return self.range2d.x_range def _set_index_range(self, newrange): self._handle_range_changed("index", self.range2d.x_range, newrange) self.range2d.x_range = newrange def _get_value_range(self): return self.range2d.y_range def _set_value_range(self, newrange): self._handle_range_changed("value", self.range2d.y_range, newrange) self.range2d.y_range = newrange def _handle_range_changed(self, name, old, new): mapper = getattr(self, name+"_mapper") if mapper.range == old: mapper.range = new if old is not None: for datasource in old.sources[:]: old.remove(datasource) if new is not None: new.add(datasource) range_name = name + "_range" for renderer in self.components: if hasattr(renderer, range_name): setattr(renderer, range_name, new) def _get_origin(self): # FIXME: return self.default_origin chaco-4.5.0/chaco/datamapper.py0000644000076600000240000002422612426452312017131 0ustar jrocherstaff00000000000000""" CAUTION: This is an old file from Chaco 1.x to support the spatial subdivision structures. It will be refactored soon. If you are looking for Chaco's mappers (subclasses of AbstractMapper), look in abstract_mapper.py, linear_mapper.py, and log_mapper.py. Defines AbstractDataMapper and BruteForceDataMapper classes, and related trait and functions. """ from numpy import array, concatenate, take, argsort, argmin, \ argmax, transpose, newaxis, sort from traits.api import HasStrictTraits, Bool, Enum, Tuple, \ Property, Any, Float #------------------------------------------------------------------- # Module-specific traits #------------------------------------------------------------------- # Expresses sorting order of ArraySortTrait = Enum('ascending', 'descending') #------------------------------------------------------------------- # Module-specific utility functions #------------------------------------------------------------------- def right_shift(ary, newval): "Returns a right-shifted version of *ary* with *newval* inserted on the left." return concatenate([[newval], ary[:-1]]) def left_shift(ary, newval): "Returns a left-shifted version of *ary* with *newval* inserted on the right." return concatenate([ary[1:], [newval]]) def sort_points(points, index=0): """ sort_points(array_of_points, index=<0|1>) -> sorted_array Takes a list of points as an Nx2 array and sorts them according to their x-coordinate (index=0) or y-coordinate (index=1). """ if len(points.shape) != 2 or (2 not in points.shape): raise RuntimeError, "sort_points(): Array of wrong shape." return take( points, argsort(points[:,index]) ) def array_zip(*arys): """ Returns a Numeric array that is the concatenation of the input 1-D *arys* along a new axis. This function is basically equivalent to ``array(zip(*arys))``, but is more resource-efficient. """ return transpose(array(arys)) class AbstractDataMapper(HasStrictTraits): """ A data mapper maps from coordinate space to data elements. In its most basic form, it loops over all the available data points to find the ones near a given coordinate or within an area. More advanced functionality includes returning rect-aligned "affected regions" enclosing all the returned points, etc. """ # How to sort the output list of intersected points that the # get_points_near_*() function returns. The points are always sorted # by their domain (first/X-value) coordinate. sort_order = ArraySortTrait # A read-only property that describes the origin and size of the data # set in data space as a 4-tuple (min_x, min_y, width, height) extents = Property() #------------------------------------------------------------------- # Private traits #------------------------------------------------------------------- _data = Any # Internally we expect Nx2 arrays; if the user hands in something # different, we stored a transposed version but always remember to # transpose once again whenever we return data. _is_transposed = Bool(False) # the max and min points in data space expressed as a 4-tuple (x,y,w,h) _extents = Tuple # a "fudge factor" to make the extents slightly larger than the actual # values in the data set _extents_delta = Float(0.1) def __init__(self, data=None, data_sorting='none', **kw): "See set_data() for description." self._data = array([]) HasStrictTraits.__init__(self, **kw) if data is not None: self.set_data(data, data_sorting) return def get_points_near(self, pointlist, radius=0.0): """ get_points_near([points], radius=0.0) -> Nx2 array of candidate points Returns a list of points near the input points (Nx2 array). For each point in the input set, *radius* is used to create a conceptual circle; if any points in the DataMapper's values lie inside this circle, they are returned. The returned list is not guaranteed to be a minimum or exact set, but it is guaranteed to contain all points that intersect the *pointlist*. The caller still must do fine-grained testing to see if the points in the returned point list are a match. """ raise NotImplementedError def get_points_near_polyline(self, line): """ get_points_near_polyline([v1, ... vN]) -> [ [points], [points], ... ] This method is like get_points_near(), except that it takes a polyline as input. A polyline is a list of vertices, each connected to the next by a straight line. The polyline has infinitely thin width. The input array can have shape 2xN or Nx2. """ raise NotImplementedError def get_points_in_rect(self, rect): """ get_points_in_rect( (x,y,w,h) ) -> [ [points], [points], ... ] This method is like get_points_near(), except that it takes a rectangle as input. The rectangle has infinitely thin width. """ raise NotImplementedError def get_points_in_poly(self, poly): """ get_points_in_poly([v1, ... vN]) -> [ [points], [points], ... ] This method is like get_points_near(), except that it takes a polygon as input. The polygon has infinitely thin width and can be self-intersecting and concave. The input array can have shape 2xN or Nx2. """ raise NotImplementedError def get_last_region(self): """ Returns a region of screen space that contains all of the points/lines/rect/polys in the last get_points_in_*() call. The region returned by this method is guaranteed to only contain the points that were returned by the previous call. The region is returned as a list of (possibly disjoint) rectangles, where each rectangle is a 4-tuple (x,y,w,h). """ raise NotImplementedError def set_data(self, new_data, new_data_sorting='none'): """ set_data(new_data, new_data_sorting='none') Sets the data used by this DataMapper. The *new_data_sorting* parameter indicates how the new data is sorted: 'none', 'ascending', or 'descending'. The default is 'none', which causes the data mapper to perform a full sort of the input data. The input data can be shaped 2xN or Nx2. """ if len(new_data) == 0: self.clear() return if new_data.shape[0] == 2: self._is_transposed = True self._data = transpose(new_data) else: self._is_transposed = False self._data = new_data if new_data_sorting == 'none': if self.sort_order == 'ascending': self._data = sort_points(self._data) else: self._data = sort_points(self._data)[::-1] elif new_data_sorting != self.sort_order: self._data = self._data[::-1] self._calc_data_extents() self._update_datamap() # a re-sorting is unnecessary because any internal data structures # will have been updated by the _data update process. return def clear(self): """ clear() Resets internal state and any cached data to reflect an empty data set/data space. """ self._data = None self._extents = (0,0,0,0) self._clear() return def get_data(self): "Returns the actual data used by the DataMapper." if self._is_transposed: return transpose(self._data) else: return self._data #------------------------------------------------------------------- # Concrete private methods and event handlers # Child classes shouldn't have to override these. #------------------------------------------------------------------- def _get_extents(self): return self._extents def _calc_data_extents(self): """ Computes ((minX, minY), (width, height)) of self._data; sets self._extent and returns nothing. """ if len(self._data) == 0: self._extents = ((0,0), (0,0)) else: value = self._data min_indices = argmin(value, axis=0) max_indices = argmax(value, axis=0) x = value[min_indices[0], 0] - self._extents_delta y = value[min_indices[1], 1] - self._extents_delta maxX = value[max_indices[0], 0] + self._extents_delta maxY = value[max_indices[1], 1] + self._extents_delta self._extents = ((x, y), (maxX-x, maxY-y)) return #------------------------------------------------------------------- # Abstract private methods and event handlers #------------------------------------------------------------------- def _update_datamap(self): """ This function gets called after self._data has changed. Child classes should implement this function if they need to recompute any cached data structures, etc. """ return def _clear(self): "Performs subclass-specific clearing/cleanup." return def _sort_order_changed(self, old, new): return class BruteForceDataMapper(AbstractDataMapper): """ The BruteForceDataMapper returns all the points, all the time. This is basically the same behavior as not having a data mapper in the pipeline at all. """ def get_points_near(self, pointlist, radius=0): return self.get_data() def get_points_near_polyline(self, line): return self.get_data() def get_points_in_rect(self, rect): return self.get_data() def get_points_in_poly(self, poly): return self.get_data() def get_last_region(self): return self._extents def _sort_order_changed(self, old, new): if len(self._data) == 0: return else: if self.sort_order == 'ascending': self._data = sort_points(self._data) else: self._data = sort_points(self._data)[::-1] return #EOF chaco-4.5.0/chaco/default_colormaps.py0000644000076600000240000151467312426462254020537 0ustar jrocherstaff00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2005-2014, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # Thanks for using Enthought open source! # # Portions of this software are: # Copyright (c) 2002-2004 John D. Hunter # All Rights Reserved. #------------------------------------------------------------------------------ """ A collection of pre-defined colormap generator functions. Each of the functions can be called with *min_value* and *max_value* parameters. In that case, they produce a Colormap which linearly maps over the specified range and has the color palette indicated by the function name. """ from numpy import array # Local imports. from .color_mapper import ColorMapper from .colormap_generators import generate_cubehelix_palette, \ generate_diverging_palette # The colormaps will be added to this at the end of the file. __all__ = ['reverse', 'center', 'color_map_functions', 'color_map_dict', 'color_map_name_dict'] # Utility functions. def reverse(func): """ Modify a colormap factory to reverse the color sequence Parameters ---------- func : callable A colormap factory function like those provided in this module. Returns ------- cmap : callable A wrapper factory function that can be used in place of the original factory function. """ def cmap(range, **traits): cm = func(range, **traits) cm.reverse_colormap() return cm # Look a little like the wrapped function. cmap.__name__ = 'reversed_' + func.__name__ if func.__doc__ is not None: cmap.__doc__ = 'Reversed: ' + func.__doc__ else: cmap.__doc__ = 'Reversed: ' + func.__name__ return cmap def center(func, center=0.0): """ Modify the range of a colormap to be centered around the given value. For example, when passed a DataRange1D(low=-0.5, high=1.0), a colormap would usually have its lowest color at -0.5 and its highest at 1.0. Some colormaps are designed such that the middle color is special. Using this modifier, the example range would be modified to -1.0 and 1.0 to make 0.0 correspond with the middle color. Parameters ---------- func : callable A colormap factory function like those provided in this module. center : float, optional The center value in dataspace. Returns ------- cmap : callable A wrapper factory function that can be used in place of the original factory function. """ def cmap(range, **traits): maxdev = max(abs(range.low - center), abs(range.high - center)) range = range.clone_traits() range.low = center - maxdev range.high = center + maxdev return func(range, **traits) # Look a little like the wrapped function. cmap.__name__ = 'centered_' + func.__name__ if func.__doc__ is not None: cmap.__doc__ = 'Centered: ' + func.__doc__ else: cmap.__doc__ = 'Centered: ' + func.__name__ return cmap def fix(func, range): """ Apply the given range to a colormap rather than accept the one coming from the data. This is useful for colormaps intrinsically tied to a given scale, like bathymetry/elevation colormaps for GIS or for working around Chaco to implement custom behavior. Paramaters ---------- func : callable A colormap factory function like those provided in this module. range : DataRange1D or (low, high) tuple. The range to apply. Returns ------- cmap : callable A wrapper factory function that can be used in place of the original factory function. """ if isinstance(range, tuple): # Adapt tuples to DataRange1D for convenience. from chaco.data_range_1d import DataRange1D range = DataRange1D(low=range[0], high=range[1]) def cmap(dummy_range, **traits): # Use the range given to the fix() function, not the cmap() function. return func(range, **traits) # Look a little like the wrapped function. cmap.__name__ = 'fixed_' + func.__name__ if func.__doc__ is not None: cmap.__doc__ = 'Fixed: ' + func.__doc__ else: cmap.__doc__ = 'Fixed: ' + func.__name__ return cmap # Colormaps. def autumn(range, **traits): """ Generator function for the 'autumn' colormap. """ _data = {'red': ((0., 1.0, 1.0),(1.0, 1.0, 1.0)), 'green': ((0., 0., 0.),(1.0, 1.0, 1.0)), 'blue': ((0., 0., 0.),(1.0, 0., 0.))} return ColorMapper.from_segment_map(_data, range=range, **traits) def bone(range, **traits): """ Generator function for the 'bone' colormap. """ _data = {'red': ((0., 0., 0.), (0.746032, 0.652778, 0.652778), (1.0, 1.0, 1.0)), 'green': ((0., 0., 0.), (0.365079, 0.319444, 0.319444), (0.746032, 0.777778, 0.777778), (1.0, 1.0, 1.0)), 'blue': ((0., 0., 0.), (0.365079, 0.444444, 0.444444), (1.0, 1.0, 1.0))} return ColorMapper.from_segment_map(_data, range=range, **traits) def cool(range, **traits): """ Generator function for the 'cool' colormap. """ _data = {'red': ((0., 0., 0.), (1.0, 1.0, 1.0)), 'green': ((0., 1., 1.), (1.0, 0., 0.)), 'blue': ((0., 1., 1.), (1.0, 1., 1.))} return ColorMapper.from_segment_map(_data, range=range, **traits) def copper(range, **traits): """ Generator function for the 'copper' colormap. """ _data = {'red': ((0., 0., 0.),(0.809524, 1.000000, 1.000000),(1.0, 1.0, 1.0)), 'green': ((0., 0., 0.),(1.0, 0.7812, 0.7812)), 'blue': ((0., 0., 0.),(1.0, 0.4975, 0.4975))} return ColorMapper.from_segment_map(_data, range=range, **traits) def flag(range, **traits): """ Generator function for the 'flag' colormap. """ _data = {'red': ((0., 1., 1.),(0.015873, 1.000000, 1.000000), (0.031746, 0.000000, 0.000000),(0.047619, 0.000000, 0.000000), (0.063492, 1.000000, 1.000000),(0.079365, 1.000000, 1.000000), (0.095238, 0.000000, 0.000000),(0.111111, 0.000000, 0.000000), (0.126984, 1.000000, 1.000000),(0.142857, 1.000000, 1.000000), (0.158730, 0.000000, 0.000000),(0.174603, 0.000000, 0.000000), (0.190476, 1.000000, 1.000000),(0.206349, 1.000000, 1.000000), (0.222222, 0.000000, 0.000000),(0.238095, 0.000000, 0.000000), (0.253968, 1.000000, 1.000000),(0.269841, 1.000000, 1.000000), (0.285714, 0.000000, 0.000000),(0.301587, 0.000000, 0.000000), (0.317460, 1.000000, 1.000000),(0.333333, 1.000000, 1.000000), (0.349206, 0.000000, 0.000000),(0.365079, 0.000000, 0.000000), (0.380952, 1.000000, 1.000000),(0.396825, 1.000000, 1.000000), (0.412698, 0.000000, 0.000000),(0.428571, 0.000000, 0.000000), (0.444444, 1.000000, 1.000000),(0.460317, 1.000000, 1.000000), (0.476190, 0.000000, 0.000000),(0.492063, 0.000000, 0.000000), (0.507937, 1.000000, 1.000000),(0.523810, 1.000000, 1.000000), (0.539683, 0.000000, 0.000000),(0.555556, 0.000000, 0.000000), (0.571429, 1.000000, 1.000000),(0.587302, 1.000000, 1.000000), (0.603175, 0.000000, 0.000000),(0.619048, 0.000000, 0.000000), (0.634921, 1.000000, 1.000000),(0.650794, 1.000000, 1.000000), (0.666667, 0.000000, 0.000000),(0.682540, 0.000000, 0.000000), (0.698413, 1.000000, 1.000000),(0.714286, 1.000000, 1.000000), (0.730159, 0.000000, 0.000000),(0.746032, 0.000000, 0.000000), (0.761905, 1.000000, 1.000000),(0.777778, 1.000000, 1.000000), (0.793651, 0.000000, 0.000000),(0.809524, 0.000000, 0.000000), (0.825397, 1.000000, 1.000000),(0.841270, 1.000000, 1.000000), (0.857143, 0.000000, 0.000000),(0.873016, 0.000000, 0.000000), (0.888889, 1.000000, 1.000000),(0.904762, 1.000000, 1.000000), (0.920635, 0.000000, 0.000000),(0.936508, 0.000000, 0.000000), (0.952381, 1.000000, 1.000000),(0.968254, 1.000000, 1.000000), (0.984127, 0.000000, 0.000000),(1.0, 0., 0.)), 'green': ((0., 0., 0.),(0.015873, 1.000000, 1.000000), (0.031746, 0.000000, 0.000000),(0.063492, 0.000000, 0.000000), (0.079365, 1.000000, 1.000000),(0.095238, 0.000000, 0.000000), (0.126984, 0.000000, 0.000000),(0.142857, 1.000000, 1.000000), (0.158730, 0.000000, 0.000000),(0.190476, 0.000000, 0.000000), (0.206349, 1.000000, 1.000000),(0.222222, 0.000000, 0.000000), (0.253968, 0.000000, 0.000000),(0.269841, 1.000000, 1.000000), (0.285714, 0.000000, 0.000000),(0.317460, 0.000000, 0.000000), (0.333333, 1.000000, 1.000000),(0.349206, 0.000000, 0.000000), (0.380952, 0.000000, 0.000000),(0.396825, 1.000000, 1.000000), (0.412698, 0.000000, 0.000000),(0.444444, 0.000000, 0.000000), (0.460317, 1.000000, 1.000000),(0.476190, 0.000000, 0.000000), (0.507937, 0.000000, 0.000000),(0.523810, 1.000000, 1.000000), (0.539683, 0.000000, 0.000000),(0.571429, 0.000000, 0.000000), (0.587302, 1.000000, 1.000000),(0.603175, 0.000000, 0.000000), (0.634921, 0.000000, 0.000000),(0.650794, 1.000000, 1.000000), (0.666667, 0.000000, 0.000000),(0.698413, 0.000000, 0.000000), (0.714286, 1.000000, 1.000000),(0.730159, 0.000000, 0.000000), (0.761905, 0.000000, 0.000000),(0.777778, 1.000000, 1.000000), (0.793651, 0.000000, 0.000000),(0.825397, 0.000000, 0.000000), (0.841270, 1.000000, 1.000000),(0.857143, 0.000000, 0.000000), (0.888889, 0.000000, 0.000000),(0.904762, 1.000000, 1.000000), (0.920635, 0.000000, 0.000000),(0.952381, 0.000000, 0.000000), (0.968254, 1.000000, 1.000000),(0.984127, 0.000000, 0.000000), (1.0, 0., 0.)), 'blue': ((0., 0., 0.),(0.015873, 1.000000, 1.000000), (0.031746, 1.000000, 1.000000),(0.047619, 0.000000, 0.000000), (0.063492, 0.000000, 0.000000),(0.079365, 1.000000, 1.000000), (0.095238, 1.000000, 1.000000),(0.111111, 0.000000, 0.000000), (0.126984, 0.000000, 0.000000),(0.142857, 1.000000, 1.000000), (0.158730, 1.000000, 1.000000),(0.174603, 0.000000, 0.000000), (0.190476, 0.000000, 0.000000),(0.206349, 1.000000, 1.000000), (0.222222, 1.000000, 1.000000),(0.238095, 0.000000, 0.000000), (0.253968, 0.000000, 0.000000),(0.269841, 1.000000, 1.000000), (0.285714, 1.000000, 1.000000),(0.301587, 0.000000, 0.000000), (0.317460, 0.000000, 0.000000),(0.333333, 1.000000, 1.000000), (0.349206, 1.000000, 1.000000),(0.365079, 0.000000, 0.000000), (0.380952, 0.000000, 0.000000),(0.396825, 1.000000, 1.000000), (0.412698, 1.000000, 1.000000),(0.428571, 0.000000, 0.000000), (0.444444, 0.000000, 0.000000),(0.460317, 1.000000, 1.000000), (0.476190, 1.000000, 1.000000),(0.492063, 0.000000, 0.000000), (0.507937, 0.000000, 0.000000),(0.523810, 1.000000, 1.000000), (0.539683, 1.000000, 1.000000),(0.555556, 0.000000, 0.000000), (0.571429, 0.000000, 0.000000),(0.587302, 1.000000, 1.000000), (0.603175, 1.000000, 1.000000),(0.619048, 0.000000, 0.000000), (0.634921, 0.000000, 0.000000),(0.650794, 1.000000, 1.000000), (0.666667, 1.000000, 1.000000),(0.682540, 0.000000, 0.000000), (0.698413, 0.000000, 0.000000),(0.714286, 1.000000, 1.000000), (0.730159, 1.000000, 1.000000),(0.746032, 0.000000, 0.000000), (0.761905, 0.000000, 0.000000),(0.777778, 1.000000, 1.000000), (0.793651, 1.000000, 1.000000),(0.809524, 0.000000, 0.000000), (0.825397, 0.000000, 0.000000),(0.841270, 1.000000, 1.000000), (0.857143, 1.000000, 1.000000),(0.873016, 0.000000, 0.000000), (0.888889, 0.000000, 0.000000),(0.904762, 1.000000, 1.000000), (0.920635, 1.000000, 1.000000),(0.936508, 0.000000, 0.000000), (0.952381, 0.000000, 0.000000),(0.968254, 1.000000, 1.000000), (0.984127, 1.000000, 1.000000),(1.0, 0., 0.))} return ColorMapper.from_segment_map(_data, range=range, **traits) def gray(range, **traits): """ Generator function for the 'gray' colormap. """ _data = {'red': ((0., 0, 0), (1., 1, 1)), 'green': ((0., 0, 0), (1., 1, 1)), 'blue': ((0., 0, 0), (1., 1, 1))} return ColorMapper.from_segment_map(_data, range=range, **traits) def yarg(range, **traits): """ Generator function for the 'yarg' colormap. """ _data = {'red': ((0., 1, 1), (1., 0, 0)), 'green': ((0., 1, 1), (1., 0, 0)), 'blue': ((0., 1, 1), (1., 0, 0))} return ColorMapper.from_segment_map(_data, range=range, **traits) def hot(range, **traits): """ Generator function for the 'hot' colormap. """ _data = {'red': ((0., 0.0416, 0.0416),(0.365079, 1.000000, 1.000000),(1.0, 1.0, 1.0)), 'green': ((0., 0., 0.),(0.365079, 0.000000, 0.000000), (0.746032, 1.000000, 1.000000),(1.0, 1.0, 1.0)), 'blue': ((0., 0., 0.),(0.746032, 0.000000, 0.000000),(1.0, 1.0, 1.0))} return ColorMapper.from_segment_map(_data, range=range, **traits) def hsv(range, **traits): """ Generator function for the 'hsv' colormap. """ _data = {'red': ((0., 1., 1.),(0.158730, 1.000000, 1.000000), (0.174603, 0.968750, 0.968750),(0.333333, 0.031250, 0.031250), (0.349206, 0.000000, 0.000000),(0.666667, 0.000000, 0.000000), (0.682540, 0.031250, 0.031250),(0.841270, 0.968750, 0.968750), (0.857143, 1.000000, 1.000000),(1.0, 1.0, 1.0)), 'green': ((0., 0., 0.),(0.158730, 0.937500, 0.937500), (0.174603, 1.000000, 1.000000),(0.507937, 1.000000, 1.000000), (0.666667, 0.062500, 0.062500),(0.682540, 0.000000, 0.000000), (1.0, 0., 0.)), 'blue': ((0., 0., 0.),(0.333333, 0.000000, 0.000000), (0.349206, 0.062500, 0.062500),(0.507937, 1.000000, 1.000000), (0.841270, 1.000000, 1.000000),(0.857143, 0.937500, 0.937500), (1.0, 0.09375, 0.09375))} return ColorMapper.from_segment_map(_data, range=range, **traits) def jet(range, **traits): """ Generator function for the 'jet' colormap. """ _data = {'red': ((0., 0, 0), (0.35, 0, 0), (0.66, 1, 1), (0.89,1, 1), (1, 0.5, 0.5)), 'green': ((0., 0, 0), (0.125,0, 0), (0.375,1, 1), (0.64,1, 1), (0.91,0,0), (1, 0, 0)), 'blue': ((0., 0.5, 0.5), (0.11, 1, 1), (0.34, 1, 1), (0.65,0, 0), (1, 0, 0))} return ColorMapper.from_segment_map(_data, range=range, **traits) def pink(range, **traits): """ Generator function for the 'pink' colormap. """ _data = {'red': ((0., 0.1178, 0.1178),(0.015873, 0.195857, 0.195857), (0.031746, 0.250661, 0.250661),(0.047619, 0.295468, 0.295468), (0.063492, 0.334324, 0.334324),(0.079365, 0.369112, 0.369112), (0.095238, 0.400892, 0.400892),(0.111111, 0.430331, 0.430331), (0.126984, 0.457882, 0.457882),(0.142857, 0.483867, 0.483867), (0.158730, 0.508525, 0.508525),(0.174603, 0.532042, 0.532042), (0.190476, 0.554563, 0.554563),(0.206349, 0.576204, 0.576204), (0.222222, 0.597061, 0.597061),(0.238095, 0.617213, 0.617213), (0.253968, 0.636729, 0.636729),(0.269841, 0.655663, 0.655663), (0.285714, 0.674066, 0.674066),(0.301587, 0.691980, 0.691980), (0.317460, 0.709441, 0.709441),(0.333333, 0.726483, 0.726483), (0.349206, 0.743134, 0.743134),(0.365079, 0.759421, 0.759421), (0.380952, 0.766356, 0.766356),(0.396825, 0.773229, 0.773229), (0.412698, 0.780042, 0.780042),(0.428571, 0.786796, 0.786796), (0.444444, 0.793492, 0.793492),(0.460317, 0.800132, 0.800132), (0.476190, 0.806718, 0.806718),(0.492063, 0.813250, 0.813250), (0.507937, 0.819730, 0.819730),(0.523810, 0.826160, 0.826160), (0.539683, 0.832539, 0.832539),(0.555556, 0.838870, 0.838870), (0.571429, 0.845154, 0.845154),(0.587302, 0.851392, 0.851392), (0.603175, 0.857584, 0.857584),(0.619048, 0.863731, 0.863731), (0.634921, 0.869835, 0.869835),(0.650794, 0.875897, 0.875897), (0.666667, 0.881917, 0.881917),(0.682540, 0.887896, 0.887896), (0.698413, 0.893835, 0.893835),(0.714286, 0.899735, 0.899735), (0.730159, 0.905597, 0.905597),(0.746032, 0.911421, 0.911421), (0.761905, 0.917208, 0.917208),(0.777778, 0.922958, 0.922958), (0.793651, 0.928673, 0.928673),(0.809524, 0.934353, 0.934353), (0.825397, 0.939999, 0.939999),(0.841270, 0.945611, 0.945611), (0.857143, 0.951190, 0.951190),(0.873016, 0.956736, 0.956736), (0.888889, 0.962250, 0.962250),(0.904762, 0.967733, 0.967733), (0.920635, 0.973185, 0.973185),(0.936508, 0.978607, 0.978607), (0.952381, 0.983999, 0.983999),(0.968254, 0.989361, 0.989361), (0.984127, 0.994695, 0.994695),(1.0, 1.0, 1.0)), 'green': ((0., 0., 0.),(0.015873, 0.102869, 0.102869), (0.031746, 0.145479, 0.145479),(0.047619, 0.178174, 0.178174), (0.063492, 0.205738, 0.205738),(0.079365, 0.230022, 0.230022), (0.095238, 0.251976, 0.251976),(0.111111, 0.272166, 0.272166), (0.126984, 0.290957, 0.290957),(0.142857, 0.308607, 0.308607), (0.158730, 0.325300, 0.325300),(0.174603, 0.341178, 0.341178), (0.190476, 0.356348, 0.356348),(0.206349, 0.370899, 0.370899), (0.222222, 0.384900, 0.384900),(0.238095, 0.398410, 0.398410), (0.253968, 0.411476, 0.411476),(0.269841, 0.424139, 0.424139), (0.285714, 0.436436, 0.436436),(0.301587, 0.448395, 0.448395), (0.317460, 0.460044, 0.460044),(0.333333, 0.471405, 0.471405), (0.349206, 0.482498, 0.482498),(0.365079, 0.493342, 0.493342), (0.380952, 0.517549, 0.517549),(0.396825, 0.540674, 0.540674), (0.412698, 0.562849, 0.562849),(0.428571, 0.584183, 0.584183), (0.444444, 0.604765, 0.604765),(0.460317, 0.624669, 0.624669), (0.476190, 0.643958, 0.643958),(0.492063, 0.662687, 0.662687), (0.507937, 0.680900, 0.680900),(0.523810, 0.698638, 0.698638), (0.539683, 0.715937, 0.715937),(0.555556, 0.732828, 0.732828), (0.571429, 0.749338, 0.749338),(0.587302, 0.765493, 0.765493), (0.603175, 0.781313, 0.781313),(0.619048, 0.796819, 0.796819), (0.634921, 0.812029, 0.812029),(0.650794, 0.826960, 0.826960), (0.666667, 0.841625, 0.841625),(0.682540, 0.856040, 0.856040), (0.698413, 0.870216, 0.870216),(0.714286, 0.884164, 0.884164), (0.730159, 0.897896, 0.897896),(0.746032, 0.911421, 0.911421), (0.761905, 0.917208, 0.917208),(0.777778, 0.922958, 0.922958), (0.793651, 0.928673, 0.928673),(0.809524, 0.934353, 0.934353), (0.825397, 0.939999, 0.939999),(0.841270, 0.945611, 0.945611), (0.857143, 0.951190, 0.951190),(0.873016, 0.956736, 0.956736), (0.888889, 0.962250, 0.962250),(0.904762, 0.967733, 0.967733), (0.920635, 0.973185, 0.973185),(0.936508, 0.978607, 0.978607), (0.952381, 0.983999, 0.983999),(0.968254, 0.989361, 0.989361), (0.984127, 0.994695, 0.994695),(1.0, 1.0, 1.0)), 'blue': ((0., 0., 0.),(0.015873, 0.102869, 0.102869), (0.031746, 0.145479, 0.145479),(0.047619, 0.178174, 0.178174), (0.063492, 0.205738, 0.205738),(0.079365, 0.230022, 0.230022), (0.095238, 0.251976, 0.251976),(0.111111, 0.272166, 0.272166), (0.126984, 0.290957, 0.290957),(0.142857, 0.308607, 0.308607), (0.158730, 0.325300, 0.325300),(0.174603, 0.341178, 0.341178), (0.190476, 0.356348, 0.356348),(0.206349, 0.370899, 0.370899), (0.222222, 0.384900, 0.384900),(0.238095, 0.398410, 0.398410), (0.253968, 0.411476, 0.411476),(0.269841, 0.424139, 0.424139), (0.285714, 0.436436, 0.436436),(0.301587, 0.448395, 0.448395), (0.317460, 0.460044, 0.460044),(0.333333, 0.471405, 0.471405), (0.349206, 0.482498, 0.482498),(0.365079, 0.493342, 0.493342), (0.380952, 0.503953, 0.503953),(0.396825, 0.514344, 0.514344), (0.412698, 0.524531, 0.524531),(0.428571, 0.534522, 0.534522), (0.444444, 0.544331, 0.544331),(0.460317, 0.553966, 0.553966), (0.476190, 0.563436, 0.563436),(0.492063, 0.572750, 0.572750), (0.507937, 0.581914, 0.581914),(0.523810, 0.590937, 0.590937), (0.539683, 0.599824, 0.599824),(0.555556, 0.608581, 0.608581), (0.571429, 0.617213, 0.617213),(0.587302, 0.625727, 0.625727), (0.603175, 0.634126, 0.634126),(0.619048, 0.642416, 0.642416), (0.634921, 0.650600, 0.650600),(0.650794, 0.658682, 0.658682), (0.666667, 0.666667, 0.666667),(0.682540, 0.674556, 0.674556), (0.698413, 0.682355, 0.682355),(0.714286, 0.690066, 0.690066), (0.730159, 0.697691, 0.697691),(0.746032, 0.705234, 0.705234), (0.761905, 0.727166, 0.727166),(0.777778, 0.748455, 0.748455), (0.793651, 0.769156, 0.769156),(0.809524, 0.789314, 0.789314), (0.825397, 0.808969, 0.808969),(0.841270, 0.828159, 0.828159), (0.857143, 0.846913, 0.846913),(0.873016, 0.865261, 0.865261), (0.888889, 0.883229, 0.883229),(0.904762, 0.900837, 0.900837), (0.920635, 0.918109, 0.918109),(0.936508, 0.935061, 0.935061), (0.952381, 0.951711, 0.951711),(0.968254, 0.968075, 0.968075), (0.984127, 0.984167, 0.984167),(1.0, 1.0, 1.0))} return ColorMapper.from_segment_map(_data, range=range, **traits) def prism(range, **traits): """ Generator function for the 'prism' colormap. """ _data = {'red': ((0., 1., 1.),(0.031746, 1.000000, 1.000000), (0.047619, 0.000000, 0.000000),(0.063492, 0.000000, 0.000000), (0.079365, 0.666667, 0.666667),(0.095238, 1.000000, 1.000000), (0.126984, 1.000000, 1.000000),(0.142857, 0.000000, 0.000000), (0.158730, 0.000000, 0.000000),(0.174603, 0.666667, 0.666667), (0.190476, 1.000000, 1.000000),(0.222222, 1.000000, 1.000000), (0.238095, 0.000000, 0.000000),(0.253968, 0.000000, 0.000000), (0.269841, 0.666667, 0.666667),(0.285714, 1.000000, 1.000000), (0.317460, 1.000000, 1.000000),(0.333333, 0.000000, 0.000000), (0.349206, 0.000000, 0.000000),(0.365079, 0.666667, 0.666667), (0.380952, 1.000000, 1.000000),(0.412698, 1.000000, 1.000000), (0.428571, 0.000000, 0.000000),(0.444444, 0.000000, 0.000000), (0.460317, 0.666667, 0.666667),(0.476190, 1.000000, 1.000000), (0.507937, 1.000000, 1.000000),(0.523810, 0.000000, 0.000000), (0.539683, 0.000000, 0.000000),(0.555556, 0.666667, 0.666667), (0.571429, 1.000000, 1.000000),(0.603175, 1.000000, 1.000000), (0.619048, 0.000000, 0.000000),(0.634921, 0.000000, 0.000000), (0.650794, 0.666667, 0.666667),(0.666667, 1.000000, 1.000000), (0.698413, 1.000000, 1.000000),(0.714286, 0.000000, 0.000000), (0.730159, 0.000000, 0.000000),(0.746032, 0.666667, 0.666667), (0.761905, 1.000000, 1.000000),(0.793651, 1.000000, 1.000000), (0.809524, 0.000000, 0.000000),(0.825397, 0.000000, 0.000000), (0.841270, 0.666667, 0.666667),(0.857143, 1.000000, 1.000000), (0.888889, 1.000000, 1.000000),(0.904762, 0.000000, 0.000000), (0.920635, 0.000000, 0.000000),(0.936508, 0.666667, 0.666667), (0.952381, 1.000000, 1.000000),(0.984127, 1.000000, 1.000000), (1.0, 0.0, 0.0)), 'green': ((0., 0., 0.),(0.031746, 1.000000, 1.000000), (0.047619, 1.000000, 1.000000),(0.063492, 0.000000, 0.000000), (0.095238, 0.000000, 0.000000),(0.126984, 1.000000, 1.000000), (0.142857, 1.000000, 1.000000),(0.158730, 0.000000, 0.000000), (0.190476, 0.000000, 0.000000),(0.222222, 1.000000, 1.000000), (0.238095, 1.000000, 1.000000),(0.253968, 0.000000, 0.000000), (0.285714, 0.000000, 0.000000),(0.317460, 1.000000, 1.000000), (0.333333, 1.000000, 1.000000),(0.349206, 0.000000, 0.000000), (0.380952, 0.000000, 0.000000),(0.412698, 1.000000, 1.000000), (0.428571, 1.000000, 1.000000),(0.444444, 0.000000, 0.000000), (0.476190, 0.000000, 0.000000),(0.507937, 1.000000, 1.000000), (0.523810, 1.000000, 1.000000),(0.539683, 0.000000, 0.000000), (0.571429, 0.000000, 0.000000),(0.603175, 1.000000, 1.000000), (0.619048, 1.000000, 1.000000),(0.634921, 0.000000, 0.000000), (0.666667, 0.000000, 0.000000),(0.698413, 1.000000, 1.000000), (0.714286, 1.000000, 1.000000),(0.730159, 0.000000, 0.000000), (0.761905, 0.000000, 0.000000),(0.793651, 1.000000, 1.000000), (0.809524, 1.000000, 1.000000),(0.825397, 0.000000, 0.000000), (0.857143, 0.000000, 0.000000),(0.888889, 1.000000, 1.000000), (0.904762, 1.000000, 1.000000),(0.920635, 0.000000, 0.000000), (0.952381, 0.000000, 0.000000),(0.984127, 1.000000, 1.000000), (1.0, 1.0, 1.0)), 'blue': ((0., 0., 0.),(0.047619, 0.000000, 0.000000), (0.063492, 1.000000, 1.000000),(0.079365, 1.000000, 1.000000), (0.095238, 0.000000, 0.000000),(0.142857, 0.000000, 0.000000), (0.158730, 1.000000, 1.000000),(0.174603, 1.000000, 1.000000), (0.190476, 0.000000, 0.000000),(0.238095, 0.000000, 0.000000), (0.253968, 1.000000, 1.000000),(0.269841, 1.000000, 1.000000), (0.285714, 0.000000, 0.000000),(0.333333, 0.000000, 0.000000), (0.349206, 1.000000, 1.000000),(0.365079, 1.000000, 1.000000), (0.380952, 0.000000, 0.000000),(0.428571, 0.000000, 0.000000), (0.444444, 1.000000, 1.000000),(0.460317, 1.000000, 1.000000), (0.476190, 0.000000, 0.000000),(0.523810, 0.000000, 0.000000), (0.539683, 1.000000, 1.000000),(0.555556, 1.000000, 1.000000), (0.571429, 0.000000, 0.000000),(0.619048, 0.000000, 0.000000), (0.634921, 1.000000, 1.000000),(0.650794, 1.000000, 1.000000), (0.666667, 0.000000, 0.000000),(0.714286, 0.000000, 0.000000), (0.730159, 1.000000, 1.000000),(0.746032, 1.000000, 1.000000), (0.761905, 0.000000, 0.000000),(0.809524, 0.000000, 0.000000), (0.825397, 1.000000, 1.000000),(0.841270, 1.000000, 1.000000), (0.857143, 0.000000, 0.000000),(0.904762, 0.000000, 0.000000), (0.920635, 1.000000, 1.000000),(0.936508, 1.000000, 1.000000), (0.952381, 0.000000, 0.000000),(1.0, 0.0, 0.0))} return ColorMapper.from_segment_map(_data, range=range, **traits) def spring(range, **traits): """ Generator function for the 'spring' colormap. """ _data = {'red': ((0., 1., 1.),(1.0, 1.0, 1.0)), 'green': ((0., 0., 0.),(1.0, 1.0, 1.0)), 'blue': ((0., 1., 1.),(1.0, 0.0, 0.0))} return ColorMapper.from_segment_map(_data, range=range, **traits) def summer(range, **traits): """ Generator function for the 'summer' colormap. """ _data = {'red': ((0., 0., 0.),(1.0, 1.0, 1.0)), 'green': ((0., 0.5, 0.5),(1.0, 1.0, 1.0)), 'blue': ((0., 0.4, 0.4),(1.0, 0.4, 0.4))} return ColorMapper.from_segment_map(_data, range=range, **traits) def winter(range, **traits): """ Generator function for the 'winter' colormap. """ _data = {'red': ((0., 0., 0.),(1.0, 0.0, 0.0)), 'green': ((0., 0., 0.),(1.0, 1.0, 1.0)), 'blue': ((0., 1., 1.),(1.0, 0.5, 0.5))} return ColorMapper.from_segment_map(_data, range=range, **traits) def cw1_004(range, **traits): """ Generator function for the Crumblingwalls cw1-004 gradient """ colors = array([(0.7176,0.6980,0.6118), (0.8000,0.5373,0.7059), (0.2510,0.4588,0.4902), (0.0588,0.3176,0.5137)]) return ColorMapper.from_palette_array(colors, range=range, **traits) def cw1_005(range, **traits): """ Generator function for the Crumblingwalls cw1-005 gradient """ colors = array([(0.7059,0.1686,0.0980), (0.7961,0.5176,0.2039), (0.2863,0.3255,0.1294)]) return ColorMapper.from_palette_array(colors, range=range, **traits) def cw1_006(range, **traits): """ Generator function for the Crumblingwalls cw1-006 gradient """ colors = array([(0.4275,0.2824,0.4667), (0.2039,0.1843,0.4667), (0.0863,0.4078,0.2078)]) return ColorMapper.from_palette_array(colors, range=range, **traits) def cw1_028(range, **traits): """ Generator function for the Crumblingwalls cw1-058 gradient """ colors = array([(0.2275, 0.2275, 0.4784), (0.3294, 0.5137, 0.8588), (0.4078, 0.8471, 0.8510)]) return ColorMapper.from_palette_array(colors, range=range, **traits) def gmt_drywet(range, **traits): _data = {'red': ((0.00000,0.5255,0.5255), (0.16670,0.9333,0.9333), (0.33330,0.7059,0.7059), (0.50000,0.1961,0.1961), (0.66670,0.0471,0.0471), (0.83330,0.1490,0.1490), (1.00000,0.0314,0.0314)), 'green': ((0.00000,0.3804,0.3804), (0.16670,0.7804,0.7804), (0.33330,0.9333,0.9333), (0.50000,0.9333,0.9333), (0.66670,0.4706,0.4706), (0.83330,0.0039,0.0039), (1.00000,0.2000,0.2000)), 'blue': ((0.00000,0.1647,0.1647), (0.16670,0.3922,0.3922), (0.33330,0.5294,0.5294), (0.50000,0.9216,0.9216), (0.66670,0.9333,0.9333), (0.83330,0.7176,0.7176), (1.00000,0.4431,0.4431)) } return ColorMapper.from_segment_map(_data, range=range, **traits) # The following colormaps come from ColorBrewer. # Only the 'Sequential' and 'Diverging' colormaps and not the 'Qualitative' # colormaps since Chaco currently does not implement qualtitative colormaps. def Blues(range, **traits): """ Generator for the 'Blues' colormap from ColorBrewer. Although the ColorBrewer colormaps are defined as discrete colormaps, we create continuous colormaps from them by linear interpolation in RGB colorspace. """ _data = dict( red = [(0.0, 0.96862745098, 0.96862745098), (0.125, 0.870588235294, 0.870588235294), (0.25, 0.776470588235, 0.776470588235), (0.375, 0.619607843137, 0.619607843137), (0.5, 0.419607843137, 0.419607843137), (0.625, 0.258823529412, 0.258823529412), (0.75, 0.129411764706, 0.129411764706), (0.875, 0.0313725490196, 0.0313725490196), (1.0, 0.0313725490196, 0.0313725490196)], green = [(0.0, 0.98431372549, 0.98431372549), (0.125, 0.921568627451, 0.921568627451), (0.25, 0.858823529412, 0.858823529412), (0.375, 0.792156862745, 0.792156862745), (0.5, 0.682352941176, 0.682352941176), (0.625, 0.572549019608, 0.572549019608), (0.75, 0.443137254902, 0.443137254902), (0.875, 0.317647058824, 0.317647058824), (1.0, 0.188235294118, 0.188235294118)], blue = [(0.0, 1.0, 1.0), (0.125, 0.96862745098, 0.96862745098), (0.25, 0.937254901961, 0.937254901961), (0.375, 0.882352941176, 0.882352941176), (0.5, 0.839215686275, 0.839215686275), (0.625, 0.776470588235, 0.776470588235), (0.75, 0.709803921569, 0.709803921569), (0.875, 0.611764705882, 0.611764705882), (1.0, 0.419607843137, 0.419607843137)], ) return ColorMapper.from_segment_map(_data, range=range, **traits) def BrBG(range, **traits): """ Generator for the 'BrBG' colormap from ColorBrewer. This is a diverging colormap. It is good for data which is centered around a "special" value, like 0. Although the ColorBrewer colormaps are defined as discrete colormaps, we create continuous colormaps from them by linear interpolation in RGB colorspace. """ _data = dict( red = [(0.0, 0.329411764706, 0.329411764706), (0.1, 0.549019607843, 0.549019607843), (0.2, 0.749019607843, 0.749019607843), (0.3, 0.874509803922, 0.874509803922), (0.4, 0.964705882353, 0.964705882353), (0.5, 0.960784313725, 0.960784313725), (0.6, 0.780392156863, 0.780392156863), (0.7, 0.501960784314, 0.501960784314), (0.8, 0.207843137255, 0.207843137255), (0.9, 0.00392156862745, 0.00392156862745), (1.0, 0.0, 0.0)], green = [(0.0, 0.188235294118, 0.188235294118), (0.1, 0.317647058824, 0.317647058824), (0.2, 0.505882352941, 0.505882352941), (0.3, 0.760784313725, 0.760784313725), (0.4, 0.909803921569, 0.909803921569), (0.5, 0.960784313725, 0.960784313725), (0.6, 0.917647058824, 0.917647058824), (0.7, 0.803921568627, 0.803921568627), (0.8, 0.592156862745, 0.592156862745), (0.9, 0.4, 0.4), (1.0, 0.235294117647, 0.235294117647)], blue = [(0.0, 0.0196078431373, 0.0196078431373), (0.1, 0.0392156862745, 0.0392156862745), (0.2, 0.176470588235, 0.176470588235), (0.3, 0.490196078431, 0.490196078431), (0.4, 0.764705882353, 0.764705882353), (0.5, 0.960784313725, 0.960784313725), (0.6, 0.898039215686, 0.898039215686), (0.7, 0.756862745098, 0.756862745098), (0.8, 0.560784313725, 0.560784313725), (0.9, 0.36862745098, 0.36862745098), (1.0, 0.188235294118, 0.188235294118)], ) return ColorMapper.from_segment_map(_data, range=range, **traits) def BuGn(range, **traits): """ Generator for the 'BuGn' colormap from ColorBrewer. Although the ColorBrewer colormaps are defined as discrete colormaps, we create continuous colormaps from them by linear interpolation in RGB colorspace. """ _data = dict( red = [(0.0, 0.96862745098, 0.96862745098), (0.125, 0.898039215686, 0.898039215686), (0.25, 0.8, 0.8), (0.375, 0.6, 0.6), (0.5, 0.4, 0.4), (0.625, 0.254901960784, 0.254901960784), (0.75, 0.137254901961, 0.137254901961), (0.875, 0.0, 0.0), (1.0, 0.0, 0.0)], green = [(0.0, 0.988235294118, 0.988235294118), (0.125, 0.960784313725, 0.960784313725), (0.25, 0.925490196078, 0.925490196078), (0.375, 0.847058823529, 0.847058823529), (0.5, 0.760784313725, 0.760784313725), (0.625, 0.682352941176, 0.682352941176), (0.75, 0.545098039216, 0.545098039216), (0.875, 0.427450980392, 0.427450980392), (1.0, 0.266666666667, 0.266666666667)], blue = [(0.0, 0.992156862745, 0.992156862745), (0.125, 0.976470588235, 0.976470588235), (0.25, 0.901960784314, 0.901960784314), (0.375, 0.788235294118, 0.788235294118), (0.5, 0.643137254902, 0.643137254902), (0.625, 0.462745098039, 0.462745098039), (0.75, 0.270588235294, 0.270588235294), (0.875, 0.172549019608, 0.172549019608), (1.0, 0.105882352941, 0.105882352941)], ) return ColorMapper.from_segment_map(_data, range=range, **traits) def BuPu(range, **traits): """ Generator for the 'BuPu' colormap from ColorBrewer. Although the ColorBrewer colormaps are defined as discrete colormaps, we create continuous colormaps from them by linear interpolation in RGB colorspace. """ _data = dict( red = [(0.0, 0.96862745098, 0.96862745098), (0.125, 0.878431372549, 0.878431372549), (0.25, 0.749019607843, 0.749019607843), (0.375, 0.619607843137, 0.619607843137), (0.5, 0.549019607843, 0.549019607843), (0.625, 0.549019607843, 0.549019607843), (0.75, 0.533333333333, 0.533333333333), (0.875, 0.505882352941, 0.505882352941), (1.0, 0.301960784314, 0.301960784314)], green = [(0.0, 0.988235294118, 0.988235294118), (0.125, 0.925490196078, 0.925490196078), (0.25, 0.827450980392, 0.827450980392), (0.375, 0.737254901961, 0.737254901961), (0.5, 0.588235294118, 0.588235294118), (0.625, 0.419607843137, 0.419607843137), (0.75, 0.254901960784, 0.254901960784), (0.875, 0.0588235294118, 0.0588235294118), (1.0, 0.0, 0.0)], blue = [(0.0, 0.992156862745, 0.992156862745), (0.125, 0.956862745098, 0.956862745098), (0.25, 0.901960784314, 0.901960784314), (0.375, 0.854901960784, 0.854901960784), (0.5, 0.776470588235, 0.776470588235), (0.625, 0.694117647059, 0.694117647059), (0.75, 0.61568627451, 0.61568627451), (0.875, 0.486274509804, 0.486274509804), (1.0, 0.294117647059, 0.294117647059)], ) return ColorMapper.from_segment_map(_data, range=range, **traits) def GnBu(range, **traits): """ Generator for the 'GnBu' colormap from ColorBrewer. Although the ColorBrewer colormaps are defined as discrete colormaps, we create continuous colormaps from them by linear interpolation in RGB colorspace. """ _data = dict( red = [(0.0, 0.96862745098, 0.96862745098), (0.125, 0.878431372549, 0.878431372549), (0.25, 0.8, 0.8), (0.375, 0.658823529412, 0.658823529412), (0.5, 0.482352941176, 0.482352941176), (0.625, 0.305882352941, 0.305882352941), (0.75, 0.16862745098, 0.16862745098), (0.875, 0.0313725490196, 0.0313725490196), (1.0, 0.0313725490196, 0.0313725490196)], green = [(0.0, 0.988235294118, 0.988235294118), (0.125, 0.952941176471, 0.952941176471), (0.25, 0.921568627451, 0.921568627451), (0.375, 0.866666666667, 0.866666666667), (0.5, 0.8, 0.8), (0.625, 0.701960784314, 0.701960784314), (0.75, 0.549019607843, 0.549019607843), (0.875, 0.407843137255, 0.407843137255), (1.0, 0.250980392157, 0.250980392157)], blue = [(0.0, 0.941176470588, 0.941176470588), (0.125, 0.858823529412, 0.858823529412), (0.25, 0.772549019608, 0.772549019608), (0.375, 0.709803921569, 0.709803921569), (0.5, 0.76862745098, 0.76862745098), (0.625, 0.827450980392, 0.827450980392), (0.75, 0.745098039216, 0.745098039216), (0.875, 0.674509803922, 0.674509803922), (1.0, 0.505882352941, 0.505882352941)], ) return ColorMapper.from_segment_map(_data, range=range, **traits) def Greens(range, **traits): """ Generator for the 'Greens' colormap from ColorBrewer. Although the ColorBrewer colormaps are defined as discrete colormaps, we create continuous colormaps from them by linear interpolation in RGB colorspace. """ _data = dict( red = [(0.0, 0.96862745098, 0.96862745098), (0.125, 0.898039215686, 0.898039215686), (0.25, 0.780392156863, 0.780392156863), (0.375, 0.63137254902, 0.63137254902), (0.5, 0.454901960784, 0.454901960784), (0.625, 0.254901960784, 0.254901960784), (0.75, 0.137254901961, 0.137254901961), (0.875, 0.0, 0.0), (1.0, 0.0, 0.0)], green = [(0.0, 0.988235294118, 0.988235294118), (0.125, 0.960784313725, 0.960784313725), (0.25, 0.913725490196, 0.913725490196), (0.375, 0.850980392157, 0.850980392157), (0.5, 0.76862745098, 0.76862745098), (0.625, 0.670588235294, 0.670588235294), (0.75, 0.545098039216, 0.545098039216), (0.875, 0.427450980392, 0.427450980392), (1.0, 0.266666666667, 0.266666666667)], blue = [(0.0, 0.960784313725, 0.960784313725), (0.125, 0.878431372549, 0.878431372549), (0.25, 0.752941176471, 0.752941176471), (0.375, 0.607843137255, 0.607843137255), (0.5, 0.462745098039, 0.462745098039), (0.625, 0.364705882353, 0.364705882353), (0.75, 0.270588235294, 0.270588235294), (0.875, 0.172549019608, 0.172549019608), (1.0, 0.105882352941, 0.105882352941)], ) return ColorMapper.from_segment_map(_data, range=range, **traits) def Greys(range, **traits): """ Generator for the 'Greys' colormap from ColorBrewer. Although the ColorBrewer colormaps are defined as discrete colormaps, we create continuous colormaps from them by linear interpolation in RGB colorspace. """ _data = dict( red = [(0.0, 1.0, 1.0), (0.125, 0.941176470588, 0.941176470588), (0.25, 0.850980392157, 0.850980392157), (0.375, 0.741176470588, 0.741176470588), (0.5, 0.588235294118, 0.588235294118), (0.625, 0.450980392157, 0.450980392157), (0.75, 0.321568627451, 0.321568627451), (0.875, 0.145098039216, 0.145098039216), (1.0, 0.0, 0.0)], green = [(0.0, 1.0, 1.0), (0.125, 0.941176470588, 0.941176470588), (0.25, 0.850980392157, 0.850980392157), (0.375, 0.741176470588, 0.741176470588), (0.5, 0.588235294118, 0.588235294118), (0.625, 0.450980392157, 0.450980392157), (0.75, 0.321568627451, 0.321568627451), (0.875, 0.145098039216, 0.145098039216), (1.0, 0.0, 0.0)], blue = [(0.0, 1.0, 1.0), (0.125, 0.941176470588, 0.941176470588), (0.25, 0.850980392157, 0.850980392157), (0.375, 0.741176470588, 0.741176470588), (0.5, 0.588235294118, 0.588235294118), (0.625, 0.450980392157, 0.450980392157), (0.75, 0.321568627451, 0.321568627451), (0.875, 0.145098039216, 0.145098039216), (1.0, 0.0, 0.0)], ) return ColorMapper.from_segment_map(_data, range=range, **traits) def OrRd(range, **traits): """ Generator for the 'OrRd' colormap from ColorBrewer. Although the ColorBrewer colormaps are defined as discrete colormaps, we create continuous colormaps from them by linear interpolation in RGB colorspace. """ _data = dict( red = [(0.0, 1.0, 1.0), (0.125, 0.996078431373, 0.996078431373), (0.25, 0.992156862745, 0.992156862745), (0.375, 0.992156862745, 0.992156862745), (0.5, 0.988235294118, 0.988235294118), (0.625, 0.937254901961, 0.937254901961), (0.75, 0.843137254902, 0.843137254902), (0.875, 0.701960784314, 0.701960784314), (1.0, 0.498039215686, 0.498039215686)], green = [(0.0, 0.96862745098, 0.96862745098), (0.125, 0.909803921569, 0.909803921569), (0.25, 0.83137254902, 0.83137254902), (0.375, 0.733333333333, 0.733333333333), (0.5, 0.552941176471, 0.552941176471), (0.625, 0.396078431373, 0.396078431373), (0.75, 0.188235294118, 0.188235294118), (0.875, 0.0, 0.0), (1.0, 0.0, 0.0)], blue = [(0.0, 0.925490196078, 0.925490196078), (0.125, 0.78431372549, 0.78431372549), (0.25, 0.619607843137, 0.619607843137), (0.375, 0.517647058824, 0.517647058824), (0.5, 0.349019607843, 0.349019607843), (0.625, 0.282352941176, 0.282352941176), (0.75, 0.121568627451, 0.121568627451), (0.875, 0.0, 0.0), (1.0, 0.0, 0.0)], ) return ColorMapper.from_segment_map(_data, range=range, **traits) def Oranges(range, **traits): """ Generator for the 'Oranges' colormap from ColorBrewer. Although the ColorBrewer colormaps are defined as discrete colormaps, we create continuous colormaps from them by linear interpolation in RGB colorspace. """ _data = dict( red = [(0.0, 1.0, 1.0), (0.125, 0.996078431373, 0.996078431373), (0.25, 0.992156862745, 0.992156862745), (0.375, 0.992156862745, 0.992156862745), (0.5, 0.992156862745, 0.992156862745), (0.625, 0.945098039216, 0.945098039216), (0.75, 0.850980392157, 0.850980392157), (0.875, 0.650980392157, 0.650980392157), (1.0, 0.498039215686, 0.498039215686)], green = [(0.0, 0.960784313725, 0.960784313725), (0.125, 0.901960784314, 0.901960784314), (0.25, 0.81568627451, 0.81568627451), (0.375, 0.682352941176, 0.682352941176), (0.5, 0.552941176471, 0.552941176471), (0.625, 0.411764705882, 0.411764705882), (0.75, 0.282352941176, 0.282352941176), (0.875, 0.211764705882, 0.211764705882), (1.0, 0.152941176471, 0.152941176471)], blue = [(0.0, 0.921568627451, 0.921568627451), (0.125, 0.807843137255, 0.807843137255), (0.25, 0.635294117647, 0.635294117647), (0.375, 0.419607843137, 0.419607843137), (0.5, 0.235294117647, 0.235294117647), (0.625, 0.0745098039216, 0.0745098039216), (0.75, 0.00392156862745, 0.00392156862745), (0.875, 0.0117647058824, 0.0117647058824), (1.0, 0.0156862745098, 0.0156862745098)], ) return ColorMapper.from_segment_map(_data, range=range, **traits) def PRGn(range, **traits): """ Generator for the 'PRGn' colormap from ColorBrewer. This is a diverging colormap. It is good for data which is centered around a "special" value, like 0. Although the ColorBrewer colormaps are defined as discrete colormaps, we create continuous colormaps from them by linear interpolation in RGB colorspace. """ _data = dict( red = [(0.0, 0.250980392157, 0.250980392157), (0.1, 0.462745098039, 0.462745098039), (0.2, 0.6, 0.6), (0.3, 0.760784313725, 0.760784313725), (0.4, 0.905882352941, 0.905882352941), (0.5, 0.96862745098, 0.96862745098), (0.6, 0.850980392157, 0.850980392157), (0.7, 0.650980392157, 0.650980392157), (0.8, 0.352941176471, 0.352941176471), (0.9, 0.105882352941, 0.105882352941), (1.0, 0.0, 0.0)], green = [(0.0, 0.0, 0.0), (0.1, 0.164705882353, 0.164705882353), (0.2, 0.439215686275, 0.439215686275), (0.3, 0.647058823529, 0.647058823529), (0.4, 0.83137254902, 0.83137254902), (0.5, 0.96862745098, 0.96862745098), (0.6, 0.941176470588, 0.941176470588), (0.7, 0.858823529412, 0.858823529412), (0.8, 0.682352941176, 0.682352941176), (0.9, 0.470588235294, 0.470588235294), (1.0, 0.266666666667, 0.266666666667)], blue = [(0.0, 0.294117647059, 0.294117647059), (0.1, 0.513725490196, 0.513725490196), (0.2, 0.670588235294, 0.670588235294), (0.3, 0.811764705882, 0.811764705882), (0.4, 0.909803921569, 0.909803921569), (0.5, 0.96862745098, 0.96862745098), (0.6, 0.827450980392, 0.827450980392), (0.7, 0.627450980392, 0.627450980392), (0.8, 0.380392156863, 0.380392156863), (0.9, 0.21568627451, 0.21568627451), (1.0, 0.105882352941, 0.105882352941)], ) return ColorMapper.from_segment_map(_data, range=range, **traits) def PiYG(range, **traits): """ Generator for the 'PiYG' colormap from ColorBrewer. This is a diverging colormap. It is good for data which is centered around a "special" value, like 0. Although the ColorBrewer colormaps are defined as discrete colormaps, we create continuous colormaps from them by linear interpolation in RGB colorspace. """ _data = dict( red = [(0.0, 0.556862745098, 0.556862745098), (0.1, 0.772549019608, 0.772549019608), (0.2, 0.870588235294, 0.870588235294), (0.3, 0.945098039216, 0.945098039216), (0.4, 0.992156862745, 0.992156862745), (0.5, 0.96862745098, 0.96862745098), (0.6, 0.901960784314, 0.901960784314), (0.7, 0.721568627451, 0.721568627451), (0.8, 0.498039215686, 0.498039215686), (0.9, 0.301960784314, 0.301960784314), (1.0, 0.152941176471, 0.152941176471)], green = [(0.0, 0.00392156862745, 0.00392156862745), (0.1, 0.105882352941, 0.105882352941), (0.2, 0.466666666667, 0.466666666667), (0.3, 0.713725490196, 0.713725490196), (0.4, 0.878431372549, 0.878431372549), (0.5, 0.96862745098, 0.96862745098), (0.6, 0.960784313725, 0.960784313725), (0.7, 0.882352941176, 0.882352941176), (0.8, 0.737254901961, 0.737254901961), (0.9, 0.572549019608, 0.572549019608), (1.0, 0.392156862745, 0.392156862745)], blue = [(0.0, 0.321568627451, 0.321568627451), (0.1, 0.490196078431, 0.490196078431), (0.2, 0.682352941176, 0.682352941176), (0.3, 0.854901960784, 0.854901960784), (0.4, 0.937254901961, 0.937254901961), (0.5, 0.96862745098, 0.96862745098), (0.6, 0.81568627451, 0.81568627451), (0.7, 0.525490196078, 0.525490196078), (0.8, 0.254901960784, 0.254901960784), (0.9, 0.129411764706, 0.129411764706), (1.0, 0.0980392156863, 0.0980392156863)], ) return ColorMapper.from_segment_map(_data, range=range, **traits) def PuBu(range, **traits): """ Generator for the 'PuBu' colormap from ColorBrewer. Although the ColorBrewer colormaps are defined as discrete colormaps, we create continuous colormaps from them by linear interpolation in RGB colorspace. """ _data = dict( red = [(0.0, 1.0, 1.0), (0.125, 0.925490196078, 0.925490196078), (0.25, 0.81568627451, 0.81568627451), (0.375, 0.650980392157, 0.650980392157), (0.5, 0.454901960784, 0.454901960784), (0.625, 0.211764705882, 0.211764705882), (0.75, 0.0196078431373, 0.0196078431373), (0.875, 0.0156862745098, 0.0156862745098), (1.0, 0.0078431372549, 0.0078431372549)], green = [(0.0, 0.96862745098, 0.96862745098), (0.125, 0.905882352941, 0.905882352941), (0.25, 0.819607843137, 0.819607843137), (0.375, 0.741176470588, 0.741176470588), (0.5, 0.662745098039, 0.662745098039), (0.625, 0.564705882353, 0.564705882353), (0.75, 0.439215686275, 0.439215686275), (0.875, 0.352941176471, 0.352941176471), (1.0, 0.219607843137, 0.219607843137)], blue = [(0.0, 0.98431372549, 0.98431372549), (0.125, 0.949019607843, 0.949019607843), (0.25, 0.901960784314, 0.901960784314), (0.375, 0.858823529412, 0.858823529412), (0.5, 0.811764705882, 0.811764705882), (0.625, 0.752941176471, 0.752941176471), (0.75, 0.690196078431, 0.690196078431), (0.875, 0.552941176471, 0.552941176471), (1.0, 0.345098039216, 0.345098039216)], ) return ColorMapper.from_segment_map(_data, range=range, **traits) def PuBuGn(range, **traits): """ Generator for the 'PuBuGn' colormap from ColorBrewer. Although the ColorBrewer colormaps are defined as discrete colormaps, we create continuous colormaps from them by linear interpolation in RGB colorspace. """ _data = dict( red = [(0.0, 1.0, 1.0), (0.125, 0.925490196078, 0.925490196078), (0.25, 0.81568627451, 0.81568627451), (0.375, 0.650980392157, 0.650980392157), (0.5, 0.403921568627, 0.403921568627), (0.625, 0.211764705882, 0.211764705882), (0.75, 0.0078431372549, 0.0078431372549), (0.875, 0.00392156862745, 0.00392156862745), (1.0, 0.00392156862745, 0.00392156862745)], green = [(0.0, 0.96862745098, 0.96862745098), (0.125, 0.886274509804, 0.886274509804), (0.25, 0.819607843137, 0.819607843137), (0.375, 0.741176470588, 0.741176470588), (0.5, 0.662745098039, 0.662745098039), (0.625, 0.564705882353, 0.564705882353), (0.75, 0.505882352941, 0.505882352941), (0.875, 0.423529411765, 0.423529411765), (1.0, 0.274509803922, 0.274509803922)], blue = [(0.0, 0.98431372549, 0.98431372549), (0.125, 0.941176470588, 0.941176470588), (0.25, 0.901960784314, 0.901960784314), (0.375, 0.858823529412, 0.858823529412), (0.5, 0.811764705882, 0.811764705882), (0.625, 0.752941176471, 0.752941176471), (0.75, 0.541176470588, 0.541176470588), (0.875, 0.349019607843, 0.349019607843), (1.0, 0.211764705882, 0.211764705882)], ) return ColorMapper.from_segment_map(_data, range=range, **traits) def PuOr(range, **traits): """ Generator for the 'PuOr' colormap from ColorBrewer. This is a diverging colormap. It is good for data which is centered around a "special" value, like 0. Although the ColorBrewer colormaps are defined as discrete colormaps, we create continuous colormaps from them by linear interpolation in RGB colorspace. """ _data = dict( red = [(0.0, 0.498039215686, 0.498039215686), (0.1, 0.701960784314, 0.701960784314), (0.2, 0.878431372549, 0.878431372549), (0.3, 0.992156862745, 0.992156862745), (0.4, 0.996078431373, 0.996078431373), (0.5, 0.96862745098, 0.96862745098), (0.6, 0.847058823529, 0.847058823529), (0.7, 0.698039215686, 0.698039215686), (0.8, 0.501960784314, 0.501960784314), (0.9, 0.329411764706, 0.329411764706), (1.0, 0.176470588235, 0.176470588235)], green = [(0.0, 0.23137254902, 0.23137254902), (0.1, 0.345098039216, 0.345098039216), (0.2, 0.509803921569, 0.509803921569), (0.3, 0.721568627451, 0.721568627451), (0.4, 0.878431372549, 0.878431372549), (0.5, 0.96862745098, 0.96862745098), (0.6, 0.854901960784, 0.854901960784), (0.7, 0.670588235294, 0.670588235294), (0.8, 0.450980392157, 0.450980392157), (0.9, 0.152941176471, 0.152941176471), (1.0, 0.0, 0.0)], blue = [(0.0, 0.0313725490196, 0.0313725490196), (0.1, 0.0235294117647, 0.0235294117647), (0.2, 0.078431372549, 0.078431372549), (0.3, 0.388235294118, 0.388235294118), (0.4, 0.713725490196, 0.713725490196), (0.5, 0.96862745098, 0.96862745098), (0.6, 0.921568627451, 0.921568627451), (0.7, 0.823529411765, 0.823529411765), (0.8, 0.674509803922, 0.674509803922), (0.9, 0.533333333333, 0.533333333333), (1.0, 0.294117647059, 0.294117647059)], ) return ColorMapper.from_segment_map(_data, range=range, **traits) def PuRd(range, **traits): """ Generator for the 'PuRd' colormap from ColorBrewer. Although the ColorBrewer colormaps are defined as discrete colormaps, we create continuous colormaps from them by linear interpolation in RGB colorspace. """ _data = dict( red = [(0.0, 0.96862745098, 0.96862745098), (0.125, 0.905882352941, 0.905882352941), (0.25, 0.83137254902, 0.83137254902), (0.375, 0.788235294118, 0.788235294118), (0.5, 0.874509803922, 0.874509803922), (0.625, 0.905882352941, 0.905882352941), (0.75, 0.807843137255, 0.807843137255), (0.875, 0.596078431373, 0.596078431373), (1.0, 0.403921568627, 0.403921568627)], green = [(0.0, 0.956862745098, 0.956862745098), (0.125, 0.882352941176, 0.882352941176), (0.25, 0.725490196078, 0.725490196078), (0.375, 0.580392156863, 0.580392156863), (0.5, 0.396078431373, 0.396078431373), (0.625, 0.160784313725, 0.160784313725), (0.75, 0.0705882352941, 0.0705882352941), (0.875, 0.0, 0.0), (1.0, 0.0, 0.0)], blue = [(0.0, 0.976470588235, 0.976470588235), (0.125, 0.937254901961, 0.937254901961), (0.25, 0.854901960784, 0.854901960784), (0.375, 0.780392156863, 0.780392156863), (0.5, 0.690196078431, 0.690196078431), (0.625, 0.541176470588, 0.541176470588), (0.75, 0.337254901961, 0.337254901961), (0.875, 0.262745098039, 0.262745098039), (1.0, 0.121568627451, 0.121568627451)], ) return ColorMapper.from_segment_map(_data, range=range, **traits) def Purples(range, **traits): """ Generator for the 'Purples' colormap from ColorBrewer. Although the ColorBrewer colormaps are defined as discrete colormaps, we create continuous colormaps from them by linear interpolation in RGB colorspace. """ _data = dict( red = [(0.0, 0.988235294118, 0.988235294118), (0.125, 0.937254901961, 0.937254901961), (0.25, 0.854901960784, 0.854901960784), (0.375, 0.737254901961, 0.737254901961), (0.5, 0.619607843137, 0.619607843137), (0.625, 0.501960784314, 0.501960784314), (0.75, 0.41568627451, 0.41568627451), (0.875, 0.329411764706, 0.329411764706), (1.0, 0.247058823529, 0.247058823529)], green = [(0.0, 0.98431372549, 0.98431372549), (0.125, 0.929411764706, 0.929411764706), (0.25, 0.854901960784, 0.854901960784), (0.375, 0.741176470588, 0.741176470588), (0.5, 0.603921568627, 0.603921568627), (0.625, 0.490196078431, 0.490196078431), (0.75, 0.317647058824, 0.317647058824), (0.875, 0.152941176471, 0.152941176471), (1.0, 0.0, 0.0)], blue = [(0.0, 0.992156862745, 0.992156862745), (0.125, 0.960784313725, 0.960784313725), (0.25, 0.921568627451, 0.921568627451), (0.375, 0.862745098039, 0.862745098039), (0.5, 0.78431372549, 0.78431372549), (0.625, 0.729411764706, 0.729411764706), (0.75, 0.639215686275, 0.639215686275), (0.875, 0.560784313725, 0.560784313725), (1.0, 0.490196078431, 0.490196078431)], ) return ColorMapper.from_segment_map(_data, range=range, **traits) def RdBu(range, **traits): """ Generator for the 'RdBu' colormap from ColorBrewer. This is a diverging colormap. It is good for data which is centered around a "special" value, like 0. Although the ColorBrewer colormaps are defined as discrete colormaps, we create continuous colormaps from them by linear interpolation in RGB colorspace. """ _data = dict( red = [(0.0, 0.403921568627, 0.403921568627), (0.1, 0.698039215686, 0.698039215686), (0.2, 0.839215686275, 0.839215686275), (0.3, 0.956862745098, 0.956862745098), (0.4, 0.992156862745, 0.992156862745), (0.5, 0.96862745098, 0.96862745098), (0.6, 0.819607843137, 0.819607843137), (0.7, 0.572549019608, 0.572549019608), (0.8, 0.262745098039, 0.262745098039), (0.9, 0.129411764706, 0.129411764706), (1.0, 0.0196078431373, 0.0196078431373)], green = [(0.0, 0.0, 0.0), (0.1, 0.0941176470588, 0.0941176470588), (0.2, 0.376470588235, 0.376470588235), (0.3, 0.647058823529, 0.647058823529), (0.4, 0.858823529412, 0.858823529412), (0.5, 0.96862745098, 0.96862745098), (0.6, 0.898039215686, 0.898039215686), (0.7, 0.772549019608, 0.772549019608), (0.8, 0.576470588235, 0.576470588235), (0.9, 0.4, 0.4), (1.0, 0.188235294118, 0.188235294118)], blue = [(0.0, 0.121568627451, 0.121568627451), (0.1, 0.16862745098, 0.16862745098), (0.2, 0.301960784314, 0.301960784314), (0.3, 0.509803921569, 0.509803921569), (0.4, 0.780392156863, 0.780392156863), (0.5, 0.96862745098, 0.96862745098), (0.6, 0.941176470588, 0.941176470588), (0.7, 0.870588235294, 0.870588235294), (0.8, 0.764705882353, 0.764705882353), (0.9, 0.674509803922, 0.674509803922), (1.0, 0.380392156863, 0.380392156863)], ) return ColorMapper.from_segment_map(_data, range=range, **traits) def RdGy(range, **traits): """ Generator for the 'RdGy' colormap from ColorBrewer. This is a diverging colormap. It is good for data which is centered around a "special" value, like 0. Although the ColorBrewer colormaps are defined as discrete colormaps, we create continuous colormaps from them by linear interpolation in RGB colorspace. """ _data = dict( red = [(0.0, 0.403921568627, 0.403921568627), (0.1, 0.698039215686, 0.698039215686), (0.2, 0.839215686275, 0.839215686275), (0.3, 0.956862745098, 0.956862745098), (0.4, 0.992156862745, 0.992156862745), (0.5, 1.0, 1.0), (0.6, 0.878431372549, 0.878431372549), (0.7, 0.729411764706, 0.729411764706), (0.8, 0.529411764706, 0.529411764706), (0.9, 0.301960784314, 0.301960784314), (1.0, 0.101960784314, 0.101960784314)], green = [(0.0, 0.0, 0.0), (0.1, 0.0941176470588, 0.0941176470588), (0.2, 0.376470588235, 0.376470588235), (0.3, 0.647058823529, 0.647058823529), (0.4, 0.858823529412, 0.858823529412), (0.5, 1.0, 1.0), (0.6, 0.878431372549, 0.878431372549), (0.7, 0.729411764706, 0.729411764706), (0.8, 0.529411764706, 0.529411764706), (0.9, 0.301960784314, 0.301960784314), (1.0, 0.101960784314, 0.101960784314)], blue = [(0.0, 0.121568627451, 0.121568627451), (0.1, 0.16862745098, 0.16862745098), (0.2, 0.301960784314, 0.301960784314), (0.3, 0.509803921569, 0.509803921569), (0.4, 0.780392156863, 0.780392156863), (0.5, 1.0, 1.0), (0.6, 0.878431372549, 0.878431372549), (0.7, 0.729411764706, 0.729411764706), (0.8, 0.529411764706, 0.529411764706), (0.9, 0.301960784314, 0.301960784314), (1.0, 0.101960784314, 0.101960784314)], ) return ColorMapper.from_segment_map(_data, range=range, **traits) def RdPu(range, **traits): """ Generator for the 'RdPu' colormap from ColorBrewer. Although the ColorBrewer colormaps are defined as discrete colormaps, we create continuous colormaps from them by linear interpolation in RGB colorspace. """ _data = dict( red = [(0.0, 1.0, 1.0), (0.125, 0.992156862745, 0.992156862745), (0.25, 0.988235294118, 0.988235294118), (0.375, 0.980392156863, 0.980392156863), (0.5, 0.96862745098, 0.96862745098), (0.625, 0.866666666667, 0.866666666667), (0.75, 0.682352941176, 0.682352941176), (0.875, 0.478431372549, 0.478431372549), (1.0, 0.286274509804, 0.286274509804)], green = [(0.0, 0.96862745098, 0.96862745098), (0.125, 0.878431372549, 0.878431372549), (0.25, 0.772549019608, 0.772549019608), (0.375, 0.623529411765, 0.623529411765), (0.5, 0.407843137255, 0.407843137255), (0.625, 0.203921568627, 0.203921568627), (0.75, 0.00392156862745, 0.00392156862745), (0.875, 0.00392156862745, 0.00392156862745), (1.0, 0.0, 0.0)], blue = [(0.0, 0.952941176471, 0.952941176471), (0.125, 0.866666666667, 0.866666666667), (0.25, 0.752941176471, 0.752941176471), (0.375, 0.709803921569, 0.709803921569), (0.5, 0.63137254902, 0.63137254902), (0.625, 0.592156862745, 0.592156862745), (0.75, 0.494117647059, 0.494117647059), (0.875, 0.466666666667, 0.466666666667), (1.0, 0.41568627451, 0.41568627451)], ) return ColorMapper.from_segment_map(_data, range=range, **traits) def RdYlBu(range, **traits): """ Generator for the 'RdYlBu' colormap from ColorBrewer. This is a diverging colormap. It is good for data which is centered around a "special" value, like 0. Although the ColorBrewer colormaps are defined as discrete colormaps, we create continuous colormaps from them by linear interpolation in RGB colorspace. """ _data = dict( red = [(0.0, 0.647058823529, 0.647058823529), (0.1, 0.843137254902, 0.843137254902), (0.2, 0.956862745098, 0.956862745098), (0.3, 0.992156862745, 0.992156862745), (0.4, 0.996078431373, 0.996078431373), (0.5, 1.0, 1.0), (0.6, 0.878431372549, 0.878431372549), (0.7, 0.670588235294, 0.670588235294), (0.8, 0.454901960784, 0.454901960784), (0.9, 0.270588235294, 0.270588235294), (1.0, 0.192156862745, 0.192156862745)], green = [(0.0, 0.0, 0.0), (0.1, 0.188235294118, 0.188235294118), (0.2, 0.427450980392, 0.427450980392), (0.3, 0.682352941176, 0.682352941176), (0.4, 0.878431372549, 0.878431372549), (0.5, 1.0, 1.0), (0.6, 0.952941176471, 0.952941176471), (0.7, 0.850980392157, 0.850980392157), (0.8, 0.678431372549, 0.678431372549), (0.9, 0.458823529412, 0.458823529412), (1.0, 0.211764705882, 0.211764705882)], blue = [(0.0, 0.149019607843, 0.149019607843), (0.1, 0.152941176471, 0.152941176471), (0.2, 0.262745098039, 0.262745098039), (0.3, 0.380392156863, 0.380392156863), (0.4, 0.564705882353, 0.564705882353), (0.5, 0.749019607843, 0.749019607843), (0.6, 0.972549019608, 0.972549019608), (0.7, 0.913725490196, 0.913725490196), (0.8, 0.819607843137, 0.819607843137), (0.9, 0.705882352941, 0.705882352941), (1.0, 0.58431372549, 0.58431372549)], ) return ColorMapper.from_segment_map(_data, range=range, **traits) def RdYlGn(range, **traits): """ Generator for the 'RdYlGn' colormap from ColorBrewer. This is a diverging colormap. It is good for data which is centered around a "special" value, like 0. Although the ColorBrewer colormaps are defined as discrete colormaps, we create continuous colormaps from them by linear interpolation in RGB colorspace. """ _data = dict( red = [(0.0, 0.647058823529, 0.647058823529), (0.1, 0.843137254902, 0.843137254902), (0.2, 0.956862745098, 0.956862745098), (0.3, 0.992156862745, 0.992156862745), (0.4, 0.996078431373, 0.996078431373), (0.5, 1.0, 1.0), (0.6, 0.850980392157, 0.850980392157), (0.7, 0.650980392157, 0.650980392157), (0.8, 0.4, 0.4), (0.9, 0.101960784314, 0.101960784314), (1.0, 0.0, 0.0)], green = [(0.0, 0.0, 0.0), (0.1, 0.188235294118, 0.188235294118), (0.2, 0.427450980392, 0.427450980392), (0.3, 0.682352941176, 0.682352941176), (0.4, 0.878431372549, 0.878431372549), (0.5, 1.0, 1.0), (0.6, 0.937254901961, 0.937254901961), (0.7, 0.850980392157, 0.850980392157), (0.8, 0.741176470588, 0.741176470588), (0.9, 0.596078431373, 0.596078431373), (1.0, 0.407843137255, 0.407843137255)], blue = [(0.0, 0.149019607843, 0.149019607843), (0.1, 0.152941176471, 0.152941176471), (0.2, 0.262745098039, 0.262745098039), (0.3, 0.380392156863, 0.380392156863), (0.4, 0.545098039216, 0.545098039216), (0.5, 0.749019607843, 0.749019607843), (0.6, 0.545098039216, 0.545098039216), (0.7, 0.41568627451, 0.41568627451), (0.8, 0.388235294118, 0.388235294118), (0.9, 0.313725490196, 0.313725490196), (1.0, 0.21568627451, 0.21568627451)], ) return ColorMapper.from_segment_map(_data, range=range, **traits) def Reds(range, **traits): """ Generator for the 'Reds' colormap from ColorBrewer. Although the ColorBrewer colormaps are defined as discrete colormaps, we create continuous colormaps from them by linear interpolation in RGB colorspace. """ _data = dict( red = [(0.0, 1.0, 1.0), (0.125, 0.996078431373, 0.996078431373), (0.25, 0.988235294118, 0.988235294118), (0.375, 0.988235294118, 0.988235294118), (0.5, 0.98431372549, 0.98431372549), (0.625, 0.937254901961, 0.937254901961), (0.75, 0.796078431373, 0.796078431373), (0.875, 0.647058823529, 0.647058823529), (1.0, 0.403921568627, 0.403921568627)], green = [(0.0, 0.960784313725, 0.960784313725), (0.125, 0.878431372549, 0.878431372549), (0.25, 0.733333333333, 0.733333333333), (0.375, 0.572549019608, 0.572549019608), (0.5, 0.41568627451, 0.41568627451), (0.625, 0.23137254902, 0.23137254902), (0.75, 0.0941176470588, 0.0941176470588), (0.875, 0.0588235294118, 0.0588235294118), (1.0, 0.0, 0.0)], blue = [(0.0, 0.941176470588, 0.941176470588), (0.125, 0.823529411765, 0.823529411765), (0.25, 0.63137254902, 0.63137254902), (0.375, 0.447058823529, 0.447058823529), (0.5, 0.290196078431, 0.290196078431), (0.625, 0.172549019608, 0.172549019608), (0.75, 0.113725490196, 0.113725490196), (0.875, 0.0823529411765, 0.0823529411765), (1.0, 0.0509803921569, 0.0509803921569)], ) return ColorMapper.from_segment_map(_data, range=range, **traits) def Spectral(range, **traits): """ Generator for the 'Spectral' colormap from ColorBrewer. This is a diverging colormap. It is good for data which is centered around a "special" value, like 0. Although the ColorBrewer colormaps are defined as discrete colormaps, we create continuous colormaps from them by linear interpolation in RGB colorspace. """ _data = dict( red = [(0.0, 0.619607843137, 0.619607843137), (0.1, 0.835294117647, 0.835294117647), (0.2, 0.956862745098, 0.956862745098), (0.3, 0.992156862745, 0.992156862745), (0.4, 0.996078431373, 0.996078431373), (0.5, 1.0, 1.0), (0.6, 0.901960784314, 0.901960784314), (0.7, 0.670588235294, 0.670588235294), (0.8, 0.4, 0.4), (0.9, 0.196078431373, 0.196078431373), (1.0, 0.36862745098, 0.36862745098)], green = [(0.0, 0.00392156862745, 0.00392156862745), (0.1, 0.243137254902, 0.243137254902), (0.2, 0.427450980392, 0.427450980392), (0.3, 0.682352941176, 0.682352941176), (0.4, 0.878431372549, 0.878431372549), (0.5, 1.0, 1.0), (0.6, 0.960784313725, 0.960784313725), (0.7, 0.866666666667, 0.866666666667), (0.8, 0.760784313725, 0.760784313725), (0.9, 0.533333333333, 0.533333333333), (1.0, 0.309803921569, 0.309803921569)], blue = [(0.0, 0.258823529412, 0.258823529412), (0.1, 0.309803921569, 0.309803921569), (0.2, 0.262745098039, 0.262745098039), (0.3, 0.380392156863, 0.380392156863), (0.4, 0.545098039216, 0.545098039216), (0.5, 0.749019607843, 0.749019607843), (0.6, 0.596078431373, 0.596078431373), (0.7, 0.643137254902, 0.643137254902), (0.8, 0.647058823529, 0.647058823529), (0.9, 0.741176470588, 0.741176470588), (1.0, 0.635294117647, 0.635294117647)], ) return ColorMapper.from_segment_map(_data, range=range, **traits) def YlGn(range, **traits): """ Generator for the 'YlGn' colormap from ColorBrewer. Although the ColorBrewer colormaps are defined as discrete colormaps, we create continuous colormaps from them by linear interpolation in RGB colorspace. """ _data = dict( red = [(0.0, 1.0, 1.0), (0.125, 0.96862745098, 0.96862745098), (0.25, 0.850980392157, 0.850980392157), (0.375, 0.678431372549, 0.678431372549), (0.5, 0.470588235294, 0.470588235294), (0.625, 0.254901960784, 0.254901960784), (0.75, 0.137254901961, 0.137254901961), (0.875, 0.0, 0.0), (1.0, 0.0, 0.0)], green = [(0.0, 1.0, 1.0), (0.125, 0.988235294118, 0.988235294118), (0.25, 0.941176470588, 0.941176470588), (0.375, 0.866666666667, 0.866666666667), (0.5, 0.776470588235, 0.776470588235), (0.625, 0.670588235294, 0.670588235294), (0.75, 0.517647058824, 0.517647058824), (0.875, 0.407843137255, 0.407843137255), (1.0, 0.270588235294, 0.270588235294)], blue = [(0.0, 0.898039215686, 0.898039215686), (0.125, 0.725490196078, 0.725490196078), (0.25, 0.639215686275, 0.639215686275), (0.375, 0.556862745098, 0.556862745098), (0.5, 0.474509803922, 0.474509803922), (0.625, 0.364705882353, 0.364705882353), (0.75, 0.262745098039, 0.262745098039), (0.875, 0.21568627451, 0.21568627451), (1.0, 0.160784313725, 0.160784313725)], ) return ColorMapper.from_segment_map(_data, range=range, **traits) def YlGnBu(range, **traits): """ Generator for the 'YlGnBu' colormap from ColorBrewer. Although the ColorBrewer colormaps are defined as discrete colormaps, we create continuous colormaps from them by linear interpolation in RGB colorspace. """ _data = dict( red = [(0.0, 1.0, 1.0), (0.125, 0.929411764706, 0.929411764706), (0.25, 0.780392156863, 0.780392156863), (0.375, 0.498039215686, 0.498039215686), (0.5, 0.254901960784, 0.254901960784), (0.625, 0.113725490196, 0.113725490196), (0.75, 0.133333333333, 0.133333333333), (0.875, 0.145098039216, 0.145098039216), (1.0, 0.0313725490196, 0.0313725490196)], green = [(0.0, 1.0, 1.0), (0.125, 0.972549019608, 0.972549019608), (0.25, 0.913725490196, 0.913725490196), (0.375, 0.803921568627, 0.803921568627), (0.5, 0.713725490196, 0.713725490196), (0.625, 0.56862745098, 0.56862745098), (0.75, 0.36862745098, 0.36862745098), (0.875, 0.203921568627, 0.203921568627), (1.0, 0.113725490196, 0.113725490196)], blue = [(0.0, 0.850980392157, 0.850980392157), (0.125, 0.694117647059, 0.694117647059), (0.25, 0.705882352941, 0.705882352941), (0.375, 0.733333333333, 0.733333333333), (0.5, 0.76862745098, 0.76862745098), (0.625, 0.752941176471, 0.752941176471), (0.75, 0.658823529412, 0.658823529412), (0.875, 0.580392156863, 0.580392156863), (1.0, 0.345098039216, 0.345098039216)], ) return ColorMapper.from_segment_map(_data, range=range, **traits) def YlOrBr(range, **traits): """ Generator for the 'YlOrBr' colormap from ColorBrewer. Although the ColorBrewer colormaps are defined as discrete colormaps, we create continuous colormaps from them by linear interpolation in RGB colorspace. """ _data = dict( red = [(0.0, 1.0, 1.0), (0.125, 1.0, 1.0), (0.25, 0.996078431373, 0.996078431373), (0.375, 0.996078431373, 0.996078431373), (0.5, 0.996078431373, 0.996078431373), (0.625, 0.925490196078, 0.925490196078), (0.75, 0.8, 0.8), (0.875, 0.6, 0.6), (1.0, 0.4, 0.4)], green = [(0.0, 1.0, 1.0), (0.125, 0.96862745098, 0.96862745098), (0.25, 0.890196078431, 0.890196078431), (0.375, 0.76862745098, 0.76862745098), (0.5, 0.6, 0.6), (0.625, 0.439215686275, 0.439215686275), (0.75, 0.298039215686, 0.298039215686), (0.875, 0.203921568627, 0.203921568627), (1.0, 0.145098039216, 0.145098039216)], blue = [(0.0, 0.898039215686, 0.898039215686), (0.125, 0.737254901961, 0.737254901961), (0.25, 0.56862745098, 0.56862745098), (0.375, 0.309803921569, 0.309803921569), (0.5, 0.160784313725, 0.160784313725), (0.625, 0.078431372549, 0.078431372549), (0.75, 0.0078431372549, 0.0078431372549), (0.875, 0.0156862745098, 0.0156862745098), (1.0, 0.0235294117647, 0.0235294117647)], ) return ColorMapper.from_segment_map(_data, range=range, **traits) def YlOrRd(range, **traits): """ Generator for the 'YlOrRd' colormap from ColorBrewer. Although the ColorBrewer colormaps are defined as discrete colormaps, we create continuous colormaps from them by linear interpolation in RGB colorspace. """ _data = dict( red = [(0.0, 1.0, 1.0), (0.125, 1.0, 1.0), (0.25, 0.996078431373, 0.996078431373), (0.375, 0.996078431373, 0.996078431373), (0.5, 0.992156862745, 0.992156862745), (0.625, 0.988235294118, 0.988235294118), (0.75, 0.890196078431, 0.890196078431), (0.875, 0.741176470588, 0.741176470588), (1.0, 0.501960784314, 0.501960784314)], green = [(0.0, 1.0, 1.0), (0.125, 0.929411764706, 0.929411764706), (0.25, 0.850980392157, 0.850980392157), (0.375, 0.698039215686, 0.698039215686), (0.5, 0.552941176471, 0.552941176471), (0.625, 0.305882352941, 0.305882352941), (0.75, 0.101960784314, 0.101960784314), (0.875, 0.0, 0.0), (1.0, 0.0, 0.0)], blue = [(0.0, 0.8, 0.8), (0.125, 0.627450980392, 0.627450980392), (0.25, 0.462745098039, 0.462745098039), (0.375, 0.298039215686, 0.298039215686), (0.5, 0.235294117647, 0.235294117647), (0.625, 0.164705882353, 0.164705882353), (0.75, 0.109803921569, 0.109803921569), (0.875, 0.149019607843, 0.149019607843), (1.0, 0.149019607843, 0.149019607843)], ) return ColorMapper.from_segment_map(_data, range=range, **traits) # The following colormaps are from Yorick, a derivative of the GIST package. def gist_earth(range, **traits): """ Generator for the 'gist_earth' colormap from GIST. """ _data = dict( red = [(0.0, 0.0, 0.0), (0.0042016808874905109, 0.0, 0.0), (0.0084033617749810219, 0.0, 0.0), (0.012605042196810246, 0.0, 0.0), (0.016806723549962044, 0.0, 0.0), (0.021008403971791267, 0.0, 0.0), (0.025210084393620491, 0.0, 0.0), (0.029411764815449715, 0.0, 0.0), (0.033613447099924088, 0.0, 0.0), (0.037815127521753311, 0.0039215688593685627, 0.0039215688593685627), (0.042016807943582535, 0.0078431377187371254, 0.0078431377187371254), (0.046218488365411758, 0.0078431377187371254, 0.0078431377187371254), (0.050420168787240982, 0.011764706112444401, 0.011764706112444401), (0.054621849209070206, 0.015686275437474251, 0.015686275437474251), (0.058823529630899429, 0.019607843831181526, 0.019607843831181526), (0.063025213778018951, 0.019607843831181526, 0.019607843831181526), (0.067226894199848175, 0.023529412224888802, 0.023529412224888802), (0.071428574621677399, 0.027450980618596077, 0.027450980618596077), (0.075630255043506622, 0.031372550874948502, 0.031372550874948502), (0.079831935465335846, 0.031372550874948502, 0.031372550874948502), (0.08403361588716507, 0.035294119268655777, 0.035294119268655777), (0.088235296308994293, 0.039215687662363052, 0.039215687662363052), (0.092436976730823517, 0.043137256056070328, 0.043137256056070328), (0.09663865715265274, 0.043137256056070328, 0.043137256056070328), (0.10084033757448196, 0.047058824449777603, 0.047058824449777603), (0.10504201799631119, 0.050980392843484879, 0.050980392843484879), (0.10924369841814041, 0.054901961237192154, 0.054901961237192154), (0.11344537883996964, 0.058823529630899429, 0.058823529630899429), (0.11764705926179886, 0.058823529630899429, 0.058823529630899429), (0.12184873968362808, 0.062745101749897003, 0.062745101749897003), (0.1260504275560379, 0.066666670143604279, 0.066666670143604279), (0.13025210797786713, 0.070588238537311554, 0.070588238537311554), (0.13445378839969635, 0.070588238537311554, 0.070588238537311554), (0.13865546882152557, 0.074509806931018829, 0.074509806931018829), (0.1428571492433548, 0.078431375324726105, 0.078431375324726105), (0.14705882966518402, 0.08235294371843338, 0.08235294371843338), (0.15126051008701324, 0.086274512112140656, 0.086274512112140656), (0.15546219050884247, 0.086274512112140656, 0.086274512112140656), (0.15966387093067169, 0.090196080505847931, 0.090196080505847931), (0.16386555135250092, 0.094117648899555206, 0.094117648899555206), (0.16806723177433014, 0.098039217293262482, 0.098039217293262482), (0.17226891219615936, 0.10196078568696976, 0.10196078568696976), (0.17647059261798859, 0.10196078568696976, 0.10196078568696976), (0.18067227303981781, 0.10588235408067703, 0.10588235408067703), (0.18487395346164703, 0.10980392247438431, 0.10980392247438431), (0.18907563388347626, 0.11372549086809158, 0.11372549086809158), (0.19327731430530548, 0.11764705926179886, 0.11764705926179886), (0.1974789947271347, 0.12156862765550613, 0.12156862765550613), (0.20168067514896393, 0.12156862765550613, 0.12156862765550613), (0.20588235557079315, 0.12549020349979401, 0.12549020349979401), (0.21008403599262238, 0.12941177189350128, 0.12941177189350128), (0.2142857164144516, 0.13333334028720856, 0.13333334028720856), (0.21848739683628082, 0.13725490868091583, 0.13725490868091583), (0.22268907725811005, 0.14117647707462311, 0.14117647707462311), (0.22689075767993927, 0.14117647707462311, 0.14117647707462311), (0.23109243810176849, 0.14509804546833038, 0.14509804546833038), (0.23529411852359772, 0.14901961386203766, 0.14901961386203766), (0.23949579894542694, 0.15294118225574493, 0.15294118225574493), (0.24369747936725616, 0.15686275064945221, 0.15686275064945221), (0.24789915978908539, 0.16078431904315948, 0.16078431904315948), (0.25210085511207581, 0.16078431904315948, 0.16078431904315948), (0.25630253553390503, 0.16470588743686676, 0.16470588743686676), (0.26050421595573425, 0.16862745583057404, 0.16862745583057404), (0.26470589637756348, 0.17254902422428131, 0.17254902422428131), (0.2689075767993927, 0.17647059261798859, 0.17647059261798859), (0.27310925722122192, 0.18039216101169586, 0.18039216101169586), (0.27731093764305115, 0.18431372940540314, 0.18431372940540314), (0.28151261806488037, 0.18823529779911041, 0.18823529779911041), (0.28571429848670959, 0.18823529779911041, 0.18823529779911041), (0.28991597890853882, 0.18823529779911041, 0.18823529779911041), (0.29411765933036804, 0.19215686619281769, 0.19215686619281769), (0.29831933975219727, 0.19215686619281769, 0.19215686619281769), (0.30252102017402649, 0.19607843458652496, 0.19607843458652496), (0.30672270059585571, 0.19607843458652496, 0.19607843458652496), (0.31092438101768494, 0.20000000298023224, 0.20000000298023224), (0.31512606143951416, 0.20000000298023224, 0.20000000298023224), (0.31932774186134338, 0.20392157137393951, 0.20392157137393951), (0.32352942228317261, 0.20392157137393951, 0.20392157137393951), (0.32773110270500183, 0.20784313976764679, 0.20784313976764679), (0.33193278312683105, 0.20784313976764679, 0.20784313976764679), (0.33613446354866028, 0.21176470816135406, 0.21176470816135406), (0.3403361439704895, 0.21176470816135406, 0.21176470816135406), (0.34453782439231873, 0.21568627655506134, 0.21568627655506134), (0.34873950481414795, 0.21568627655506134, 0.21568627655506134), (0.35294118523597717, 0.21960784494876862, 0.21960784494876862), (0.3571428656578064, 0.21960784494876862, 0.21960784494876862), (0.36134454607963562, 0.22352941334247589, 0.22352941334247589), (0.36554622650146484, 0.22352941334247589, 0.22352941334247589), (0.36974790692329407, 0.22745098173618317, 0.22745098173618317), (0.37394958734512329, 0.22745098173618317, 0.22745098173618317), (0.37815126776695251, 0.23137255012989044, 0.23137255012989044), (0.38235294818878174, 0.23137255012989044, 0.23137255012989044), (0.38655462861061096, 0.23529411852359772, 0.23529411852359772), (0.39075630903244019, 0.23921568691730499, 0.23921568691730499), (0.39495798945426941, 0.23921568691730499, 0.23921568691730499), (0.39915966987609863, 0.24313725531101227, 0.24313725531101227), (0.40336135029792786, 0.24313725531101227, 0.24313725531101227), (0.40756303071975708, 0.24705882370471954, 0.24705882370471954), (0.4117647111415863, 0.24705882370471954, 0.24705882370471954), (0.41596639156341553, 0.25098040699958801, 0.25098040699958801), (0.42016807198524475, 0.25098040699958801, 0.25098040699958801), (0.42436975240707397, 0.25490197539329529, 0.25490197539329529), (0.4285714328289032, 0.25490197539329529, 0.25490197539329529), (0.43277311325073242, 0.25882354378700256, 0.25882354378700256), (0.43697479367256165, 0.26274511218070984, 0.26274511218070984), (0.44117647409439087, 0.26274511218070984, 0.26274511218070984), (0.44537815451622009, 0.26666668057441711, 0.26666668057441711), (0.44957983493804932, 0.26666668057441711, 0.26666668057441711), (0.45378151535987854, 0.27058824896812439, 0.27058824896812439), (0.45798319578170776, 0.27058824896812439, 0.27058824896812439), (0.46218487620353699, 0.27450981736183167, 0.27450981736183167), (0.46638655662536621, 0.27843138575553894, 0.27843138575553894), (0.47058823704719543, 0.28627452254295349, 0.28627452254295349), (0.47478991746902466, 0.29803922772407532, 0.29803922772407532), (0.47899159789085388, 0.30588236451148987, 0.30588236451148987), (0.48319327831268311, 0.31764706969261169, 0.31764706969261169), (0.48739495873451233, 0.32549020648002625, 0.32549020648002625), (0.49159663915634155, 0.33725491166114807, 0.33725491166114807), (0.49579831957817078, 0.34509804844856262, 0.34509804844856262), (0.5, 0.35686275362968445, 0.35686275362968445), (0.50420171022415161, 0.36862745881080627, 0.36862745881080627), (0.50840336084365845, 0.37647059559822083, 0.37647059559822083), (0.51260507106781006, 0.38823530077934265, 0.38823530077934265), (0.51680672168731689, 0.3960784375667572, 0.3960784375667572), (0.52100843191146851, 0.40784314274787903, 0.40784314274787903), (0.52521008253097534, 0.41568627953529358, 0.41568627953529358), (0.52941179275512695, 0.42745098471641541, 0.42745098471641541), (0.53361344337463379, 0.43529412150382996, 0.43529412150382996), (0.5378151535987854, 0.44705882668495178, 0.44705882668495178), (0.54201680421829224, 0.45882353186607361, 0.45882353186607361), (0.54621851444244385, 0.46666666865348816, 0.46666666865348816), (0.55042016506195068, 0.47450980544090271, 0.47450980544090271), (0.55462187528610229, 0.47843137383460999, 0.47843137383460999), (0.55882352590560913, 0.48627451062202454, 0.48627451062202454), (0.56302523612976074, 0.49411764740943909, 0.49411764740943909), (0.56722688674926758, 0.50196081399917603, 0.50196081399917603), (0.57142859697341919, 0.5058823823928833, 0.5058823823928833), (0.57563024759292603, 0.51372551918029785, 0.51372551918029785), (0.57983195781707764, 0.5215686559677124, 0.5215686559677124), (0.58403360843658447, 0.52941179275512695, 0.52941179275512695), (0.58823531866073608, 0.53333336114883423, 0.53333336114883423), (0.59243696928024292, 0.54117649793624878, 0.54117649793624878), (0.59663867950439453, 0.54901963472366333, 0.54901963472366333), (0.60084033012390137, 0.55294120311737061, 0.55294120311737061), (0.60504204034805298, 0.56078433990478516, 0.56078433990478516), (0.60924369096755981, 0.56862747669219971, 0.56862747669219971), (0.61344540119171143, 0.57647061347961426, 0.57647061347961426), (0.61764705181121826, 0.58431375026702881, 0.58431375026702881), (0.62184876203536987, 0.58823531866073608, 0.58823531866073608), (0.62605041265487671, 0.59607845544815063, 0.59607845544815063), (0.63025212287902832, 0.60392159223556519, 0.60392159223556519), (0.63445377349853516, 0.61176472902297974, 0.61176472902297974), (0.63865548372268677, 0.61568629741668701, 0.61568629741668701), (0.6428571343421936, 0.62352943420410156, 0.62352943420410156), (0.64705884456634521, 0.63137257099151611, 0.63137257099151611), (0.65126049518585205, 0.63921570777893066, 0.63921570777893066), (0.65546220541000366, 0.64705884456634521, 0.64705884456634521), (0.6596638560295105, 0.65098041296005249, 0.65098041296005249), (0.66386556625366211, 0.65882354974746704, 0.65882354974746704), (0.66806721687316895, 0.66666668653488159, 0.66666668653488159), (0.67226892709732056, 0.67450982332229614, 0.67450982332229614), (0.67647057771682739, 0.68235296010971069, 0.68235296010971069), (0.680672287940979, 0.68627452850341797, 0.68627452850341797), (0.68487393856048584, 0.69411766529083252, 0.69411766529083252), (0.68907564878463745, 0.70196080207824707, 0.70196080207824707), (0.69327729940414429, 0.70980393886566162, 0.70980393886566162), (0.6974790096282959, 0.71764707565307617, 0.71764707565307617), (0.70168066024780273, 0.71764707565307617, 0.71764707565307617), (0.70588237047195435, 0.72156864404678345, 0.72156864404678345), (0.71008402109146118, 0.72156864404678345, 0.72156864404678345), (0.71428573131561279, 0.72549021244049072, 0.72549021244049072), (0.71848738193511963, 0.72549021244049072, 0.72549021244049072), (0.72268909215927124, 0.729411780834198, 0.729411780834198), (0.72689074277877808, 0.729411780834198, 0.729411780834198), (0.73109245300292969, 0.73333334922790527, 0.73333334922790527), (0.73529410362243652, 0.73333334922790527, 0.73333334922790527), (0.73949581384658813, 0.73333334922790527, 0.73333334922790527), (0.74369746446609497, 0.73725491762161255, 0.73725491762161255), (0.74789917469024658, 0.73725491762161255, 0.73725491762161255), (0.75210082530975342, 0.74117648601531982, 0.74117648601531982), (0.75630253553390503, 0.74117648601531982, 0.74117648601531982), (0.76050418615341187, 0.7450980544090271, 0.7450980544090271), (0.76470589637756348, 0.7450980544090271, 0.7450980544090271), (0.76890754699707031, 0.7450980544090271, 0.7450980544090271), (0.77310925722122192, 0.74901962280273438, 0.74901962280273438), (0.77731090784072876, 0.74901962280273438, 0.74901962280273438), (0.78151261806488037, 0.75294119119644165, 0.75294119119644165), (0.78571426868438721, 0.75294119119644165, 0.75294119119644165), (0.78991597890853882, 0.75686275959014893, 0.75686275959014893), (0.79411762952804565, 0.76470589637756348, 0.76470589637756348), (0.79831933975219727, 0.76862746477127075, 0.76862746477127075), (0.8025209903717041, 0.77254903316497803, 0.77254903316497803), (0.80672270059585571, 0.7764706015586853, 0.7764706015586853), (0.81092435121536255, 0.78039216995239258, 0.78039216995239258), (0.81512606143951416, 0.78823530673980713, 0.78823530673980713), (0.819327712059021, 0.7921568751335144, 0.7921568751335144), (0.82352942228317261, 0.79607844352722168, 0.79607844352722168), (0.82773107290267944, 0.80000001192092896, 0.80000001192092896), (0.83193278312683105, 0.80392158031463623, 0.80392158031463623), (0.83613443374633789, 0.81176471710205078, 0.81176471710205078), (0.8403361439704895, 0.81568628549575806, 0.81568628549575806), (0.84453779458999634, 0.81960785388946533, 0.81960785388946533), (0.84873950481414795, 0.82352942228317261, 0.82352942228317261), (0.85294115543365479, 0.82745099067687988, 0.82745099067687988), (0.8571428656578064, 0.83529412746429443, 0.83529412746429443), (0.86134451627731323, 0.83921569585800171, 0.83921569585800171), (0.86554622650146484, 0.84313726425170898, 0.84313726425170898), (0.86974787712097168, 0.84705883264541626, 0.84705883264541626), (0.87394958734512329, 0.85098040103912354, 0.85098040103912354), (0.87815123796463013, 0.85882353782653809, 0.85882353782653809), (0.88235294818878174, 0.86274510622024536, 0.86274510622024536), (0.88655459880828857, 0.86666667461395264, 0.86666667461395264), (0.89075630903244019, 0.87058824300765991, 0.87058824300765991), (0.89495795965194702, 0.87450981140136719, 0.87450981140136719), (0.89915966987609863, 0.88235294818878174, 0.88235294818878174), (0.90336132049560547, 0.88627451658248901, 0.88627451658248901), (0.90756303071975708, 0.89019608497619629, 0.89019608497619629), (0.91176468133926392, 0.89411765336990356, 0.89411765336990356), (0.91596639156341553, 0.89803922176361084, 0.89803922176361084), (0.92016804218292236, 0.90588235855102539, 0.90588235855102539), (0.92436975240707397, 0.90980392694473267, 0.90980392694473267), (0.92857140302658081, 0.91372549533843994, 0.91372549533843994), (0.93277311325073242, 0.91764706373214722, 0.91764706373214722), (0.93697476387023926, 0.92156863212585449, 0.92156863212585449), (0.94117647409439087, 0.92941176891326904, 0.92941176891326904), (0.94537812471389771, 0.93333333730697632, 0.93333333730697632), (0.94957983493804932, 0.93725490570068359, 0.93725490570068359), (0.95378148555755615, 0.94117647409439087, 0.94117647409439087), (0.95798319578170776, 0.94509804248809814, 0.94509804248809814), (0.9621848464012146, 0.9529411792755127, 0.9529411792755127), (0.96638655662536621, 0.95686274766921997, 0.95686274766921997), (0.97058820724487305, 0.96078431606292725, 0.96078431606292725), (0.97478991746902466, 0.96470588445663452, 0.96470588445663452), (0.97899156808853149, 0.9686274528503418, 0.9686274528503418), (0.98319327831268311, 0.97647058963775635, 0.97647058963775635), (0.98739492893218994, 0.98039215803146362, 0.98039215803146362), (0.99159663915634155, 0.9843137264251709, 0.9843137264251709), (0.99579828977584839, 0.98823529481887817, 0.98823529481887817), (1.0, 0.99215686321258545, 0.99215686321258545)], green = [(0.0, 0.0, 0.0), (0.0042016808874905109, 0.0, 0.0), (0.0084033617749810219, 0.0, 0.0), (0.012605042196810246, 0.0, 0.0), (0.016806723549962044, 0.0, 0.0), (0.021008403971791267, 0.0, 0.0), (0.025210084393620491, 0.0, 0.0), (0.029411764815449715, 0.0, 0.0), (0.033613447099924088, 0.011764706112444401, 0.011764706112444401), (0.037815127521753311, 0.023529412224888802, 0.023529412224888802), (0.042016807943582535, 0.031372550874948502, 0.031372550874948502), (0.046218488365411758, 0.043137256056070328, 0.043137256056070328), (0.050420168787240982, 0.050980392843484879, 0.050980392843484879), (0.054621849209070206, 0.062745101749897003, 0.062745101749897003), (0.058823529630899429, 0.070588238537311554, 0.070588238537311554), (0.063025213778018951, 0.08235294371843338, 0.08235294371843338), (0.067226894199848175, 0.090196080505847931, 0.090196080505847931), (0.071428574621677399, 0.10196078568696976, 0.10196078568696976), (0.075630255043506622, 0.10980392247438431, 0.10980392247438431), (0.079831935465335846, 0.12156862765550613, 0.12156862765550613), (0.08403361588716507, 0.12941177189350128, 0.12941177189350128), (0.088235296308994293, 0.14117647707462311, 0.14117647707462311), (0.092436976730823517, 0.14901961386203766, 0.14901961386203766), (0.09663865715265274, 0.16078431904315948, 0.16078431904315948), (0.10084033757448196, 0.16862745583057404, 0.16862745583057404), (0.10504201799631119, 0.17647059261798859, 0.17647059261798859), (0.10924369841814041, 0.18823529779911041, 0.18823529779911041), (0.11344537883996964, 0.19607843458652496, 0.19607843458652496), (0.11764705926179886, 0.20392157137393951, 0.20392157137393951), (0.12184873968362808, 0.21568627655506134, 0.21568627655506134), (0.1260504275560379, 0.22352941334247589, 0.22352941334247589), (0.13025210797786713, 0.23137255012989044, 0.23137255012989044), (0.13445378839969635, 0.23921568691730499, 0.23921568691730499), (0.13865546882152557, 0.25098040699958801, 0.25098040699958801), (0.1428571492433548, 0.25882354378700256, 0.25882354378700256), (0.14705882966518402, 0.26666668057441711, 0.26666668057441711), (0.15126051008701324, 0.27450981736183167, 0.27450981736183167), (0.15546219050884247, 0.28235295414924622, 0.28235295414924622), (0.15966387093067169, 0.29019609093666077, 0.29019609093666077), (0.16386555135250092, 0.30196079611778259, 0.30196079611778259), (0.16806723177433014, 0.30980393290519714, 0.30980393290519714), (0.17226891219615936, 0.31764706969261169, 0.31764706969261169), (0.17647059261798859, 0.32549020648002625, 0.32549020648002625), (0.18067227303981781, 0.3333333432674408, 0.3333333432674408), (0.18487395346164703, 0.34117648005485535, 0.34117648005485535), (0.18907563388347626, 0.3490196168422699, 0.3490196168422699), (0.19327731430530548, 0.35686275362968445, 0.35686275362968445), (0.1974789947271347, 0.364705890417099, 0.364705890417099), (0.20168067514896393, 0.37254902720451355, 0.37254902720451355), (0.20588235557079315, 0.3803921639919281, 0.3803921639919281), (0.21008403599262238, 0.38823530077934265, 0.38823530077934265), (0.2142857164144516, 0.39215686917304993, 0.39215686917304993), (0.21848739683628082, 0.40000000596046448, 0.40000000596046448), (0.22268907725811005, 0.40784314274787903, 0.40784314274787903), (0.22689075767993927, 0.41568627953529358, 0.41568627953529358), (0.23109243810176849, 0.42352941632270813, 0.42352941632270813), (0.23529411852359772, 0.42745098471641541, 0.42745098471641541), (0.23949579894542694, 0.43529412150382996, 0.43529412150382996), (0.24369747936725616, 0.44313725829124451, 0.44313725829124451), (0.24789915978908539, 0.45098039507865906, 0.45098039507865906), (0.25210085511207581, 0.45490196347236633, 0.45490196347236633), (0.25630253553390503, 0.46274510025978088, 0.46274510025978088), (0.26050421595573425, 0.47058823704719543, 0.47058823704719543), (0.26470589637756348, 0.47450980544090271, 0.47450980544090271), (0.2689075767993927, 0.48235294222831726, 0.48235294222831726), (0.27310925722122192, 0.49019607901573181, 0.49019607901573181), (0.27731093764305115, 0.49411764740943909, 0.49411764740943909), (0.28151261806488037, 0.50196081399917603, 0.50196081399917603), (0.28571429848670959, 0.50196081399917603, 0.50196081399917603), (0.28991597890853882, 0.5058823823928833, 0.5058823823928833), (0.29411765933036804, 0.5058823823928833, 0.5058823823928833), (0.29831933975219727, 0.50980395078659058, 0.50980395078659058), (0.30252102017402649, 0.51372551918029785, 0.51372551918029785), (0.30672270059585571, 0.51372551918029785, 0.51372551918029785), (0.31092438101768494, 0.51764708757400513, 0.51764708757400513), (0.31512606143951416, 0.5215686559677124, 0.5215686559677124), (0.31932774186134338, 0.5215686559677124, 0.5215686559677124), (0.32352942228317261, 0.52549022436141968, 0.52549022436141968), (0.32773110270500183, 0.52549022436141968, 0.52549022436141968), (0.33193278312683105, 0.52941179275512695, 0.52941179275512695), (0.33613446354866028, 0.53333336114883423, 0.53333336114883423), (0.3403361439704895, 0.53333336114883423, 0.53333336114883423), (0.34453782439231873, 0.5372549295425415, 0.5372549295425415), (0.34873950481414795, 0.54117649793624878, 0.54117649793624878), (0.35294118523597717, 0.54117649793624878, 0.54117649793624878), (0.3571428656578064, 0.54509806632995605, 0.54509806632995605), (0.36134454607963562, 0.54901963472366333, 0.54901963472366333), (0.36554622650146484, 0.54901963472366333, 0.54901963472366333), (0.36974790692329407, 0.55294120311737061, 0.55294120311737061), (0.37394958734512329, 0.55294120311737061, 0.55294120311737061), (0.37815126776695251, 0.55686277151107788, 0.55686277151107788), (0.38235294818878174, 0.56078433990478516, 0.56078433990478516), (0.38655462861061096, 0.56078433990478516, 0.56078433990478516), (0.39075630903244019, 0.56470590829849243, 0.56470590829849243), (0.39495798945426941, 0.56862747669219971, 0.56862747669219971), (0.39915966987609863, 0.56862747669219971, 0.56862747669219971), (0.40336135029792786, 0.57254904508590698, 0.57254904508590698), (0.40756303071975708, 0.57254904508590698, 0.57254904508590698), (0.4117647111415863, 0.57647061347961426, 0.57647061347961426), (0.41596639156341553, 0.58039218187332153, 0.58039218187332153), (0.42016807198524475, 0.58039218187332153, 0.58039218187332153), (0.42436975240707397, 0.58431375026702881, 0.58431375026702881), (0.4285714328289032, 0.58823531866073608, 0.58823531866073608), (0.43277311325073242, 0.58823531866073608, 0.58823531866073608), (0.43697479367256165, 0.59215688705444336, 0.59215688705444336), (0.44117647409439087, 0.59215688705444336, 0.59215688705444336), (0.44537815451622009, 0.59607845544815063, 0.59607845544815063), (0.44957983493804932, 0.60000002384185791, 0.60000002384185791), (0.45378151535987854, 0.60000002384185791, 0.60000002384185791), (0.45798319578170776, 0.60392159223556519, 0.60392159223556519), (0.46218487620353699, 0.60784316062927246, 0.60784316062927246), (0.46638655662536621, 0.60784316062927246, 0.60784316062927246), (0.47058823704719543, 0.61176472902297974, 0.61176472902297974), (0.47478991746902466, 0.61176472902297974, 0.61176472902297974), (0.47899159789085388, 0.61568629741668701, 0.61568629741668701), (0.48319327831268311, 0.61960786581039429, 0.61960786581039429), (0.48739495873451233, 0.61960786581039429, 0.61960786581039429), (0.49159663915634155, 0.62352943420410156, 0.62352943420410156), (0.49579831957817078, 0.62745100259780884, 0.62745100259780884), (0.5, 0.62745100259780884, 0.62745100259780884), (0.50420171022415161, 0.63137257099151611, 0.63137257099151611), (0.50840336084365845, 0.63137257099151611, 0.63137257099151611), (0.51260507106781006, 0.63529413938522339, 0.63529413938522339), (0.51680672168731689, 0.63921570777893066, 0.63921570777893066), (0.52100843191146851, 0.63921570777893066, 0.63921570777893066), (0.52521008253097534, 0.64313727617263794, 0.64313727617263794), (0.52941179275512695, 0.64705884456634521, 0.64705884456634521), (0.53361344337463379, 0.64705884456634521, 0.64705884456634521), (0.5378151535987854, 0.65098041296005249, 0.65098041296005249), (0.54201680421829224, 0.65098041296005249, 0.65098041296005249), (0.54621851444244385, 0.65490198135375977, 0.65490198135375977), (0.55042016506195068, 0.65882354974746704, 0.65882354974746704), (0.55462187528610229, 0.65882354974746704, 0.65882354974746704), (0.55882352590560913, 0.65882354974746704, 0.65882354974746704), (0.56302523612976074, 0.66274511814117432, 0.66274511814117432), (0.56722688674926758, 0.66274511814117432, 0.66274511814117432), (0.57142859697341919, 0.66666668653488159, 0.66666668653488159), (0.57563024759292603, 0.66666668653488159, 0.66666668653488159), (0.57983195781707764, 0.67058825492858887, 0.67058825492858887), (0.58403360843658447, 0.67058825492858887, 0.67058825492858887), (0.58823531866073608, 0.67450982332229614, 0.67450982332229614), (0.59243696928024292, 0.67450982332229614, 0.67450982332229614), (0.59663867950439453, 0.67450982332229614, 0.67450982332229614), (0.60084033012390137, 0.67843139171600342, 0.67843139171600342), (0.60504204034805298, 0.67843139171600342, 0.67843139171600342), (0.60924369096755981, 0.68235296010971069, 0.68235296010971069), (0.61344540119171143, 0.68235296010971069, 0.68235296010971069), (0.61764705181121826, 0.68627452850341797, 0.68627452850341797), (0.62184876203536987, 0.68627452850341797, 0.68627452850341797), (0.62605041265487671, 0.68627452850341797, 0.68627452850341797), (0.63025212287902832, 0.69019609689712524, 0.69019609689712524), (0.63445377349853516, 0.69019609689712524, 0.69019609689712524), (0.63865548372268677, 0.69411766529083252, 0.69411766529083252), (0.6428571343421936, 0.69411766529083252, 0.69411766529083252), (0.64705884456634521, 0.69803923368453979, 0.69803923368453979), (0.65126049518585205, 0.69803923368453979, 0.69803923368453979), (0.65546220541000366, 0.70196080207824707, 0.70196080207824707), (0.6596638560295105, 0.70196080207824707, 0.70196080207824707), (0.66386556625366211, 0.70196080207824707, 0.70196080207824707), (0.66806721687316895, 0.70588237047195435, 0.70588237047195435), (0.67226892709732056, 0.70588237047195435, 0.70588237047195435), (0.67647057771682739, 0.70980393886566162, 0.70980393886566162), (0.680672287940979, 0.70980393886566162, 0.70980393886566162), (0.68487393856048584, 0.7137255072593689, 0.7137255072593689), (0.68907564878463745, 0.7137255072593689, 0.7137255072593689), (0.69327729940414429, 0.71764707565307617, 0.71764707565307617), (0.6974790096282959, 0.71764707565307617, 0.71764707565307617), (0.70168066024780273, 0.7137255072593689, 0.7137255072593689), (0.70588237047195435, 0.70980393886566162, 0.70980393886566162), (0.71008402109146118, 0.70980393886566162, 0.70980393886566162), (0.71428573131561279, 0.70588237047195435, 0.70588237047195435), (0.71848738193511963, 0.70196080207824707, 0.70196080207824707), (0.72268909215927124, 0.69803923368453979, 0.69803923368453979), (0.72689074277877808, 0.69411766529083252, 0.69411766529083252), (0.73109245300292969, 0.69019609689712524, 0.69019609689712524), (0.73529410362243652, 0.68627452850341797, 0.68627452850341797), (0.73949581384658813, 0.68235296010971069, 0.68235296010971069), (0.74369746446609497, 0.67843139171600342, 0.67843139171600342), (0.74789917469024658, 0.67450982332229614, 0.67450982332229614), (0.75210082530975342, 0.67058825492858887, 0.67058825492858887), (0.75630253553390503, 0.66666668653488159, 0.66666668653488159), (0.76050418615341187, 0.66274511814117432, 0.66274511814117432), (0.76470589637756348, 0.65882354974746704, 0.65882354974746704), (0.76890754699707031, 0.65490198135375977, 0.65490198135375977), (0.77310925722122192, 0.65098041296005249, 0.65098041296005249), (0.77731090784072876, 0.64705884456634521, 0.64705884456634521), (0.78151261806488037, 0.64313727617263794, 0.64313727617263794), (0.78571426868438721, 0.63921570777893066, 0.63921570777893066), (0.78991597890853882, 0.63921570777893066, 0.63921570777893066), (0.79411762952804565, 0.64313727617263794, 0.64313727617263794), (0.79831933975219727, 0.64313727617263794, 0.64313727617263794), (0.8025209903717041, 0.64705884456634521, 0.64705884456634521), (0.80672270059585571, 0.64705884456634521, 0.64705884456634521), (0.81092435121536255, 0.65098041296005249, 0.65098041296005249), (0.81512606143951416, 0.65490198135375977, 0.65490198135375977), (0.819327712059021, 0.65490198135375977, 0.65490198135375977), (0.82352942228317261, 0.65882354974746704, 0.65882354974746704), (0.82773107290267944, 0.66274511814117432, 0.66274511814117432), (0.83193278312683105, 0.66666668653488159, 0.66666668653488159), (0.83613443374633789, 0.67058825492858887, 0.67058825492858887), (0.8403361439704895, 0.67450982332229614, 0.67450982332229614), (0.84453779458999634, 0.67843139171600342, 0.67843139171600342), (0.84873950481414795, 0.68235296010971069, 0.68235296010971069), (0.85294115543365479, 0.68627452850341797, 0.68627452850341797), (0.8571428656578064, 0.69019609689712524, 0.69019609689712524), (0.86134451627731323, 0.69411766529083252, 0.69411766529083252), (0.86554622650146484, 0.69803923368453979, 0.69803923368453979), (0.86974787712097168, 0.70196080207824707, 0.70196080207824707), (0.87394958734512329, 0.70980393886566162, 0.70980393886566162), (0.87815123796463013, 0.7137255072593689, 0.7137255072593689), (0.88235294818878174, 0.72156864404678345, 0.72156864404678345), (0.88655459880828857, 0.72549021244049072, 0.72549021244049072), (0.89075630903244019, 0.73333334922790527, 0.73333334922790527), (0.89495795965194702, 0.73725491762161255, 0.73725491762161255), (0.89915966987609863, 0.7450980544090271, 0.7450980544090271), (0.90336132049560547, 0.75294119119644165, 0.75294119119644165), (0.90756303071975708, 0.7607843279838562, 0.7607843279838562), (0.91176468133926392, 0.76862746477127075, 0.76862746477127075), (0.91596639156341553, 0.7764706015586853, 0.7764706015586853), (0.92016804218292236, 0.78431373834609985, 0.78431373834609985), (0.92436975240707397, 0.7921568751335144, 0.7921568751335144), (0.92857140302658081, 0.80000001192092896, 0.80000001192092896), (0.93277311325073242, 0.80784314870834351, 0.80784314870834351), (0.93697476387023926, 0.81568628549575806, 0.81568628549575806), (0.94117647409439087, 0.82745099067687988, 0.82745099067687988), (0.94537812471389771, 0.83529412746429443, 0.83529412746429443), (0.94957983493804932, 0.84313726425170898, 0.84313726425170898), (0.95378148555755615, 0.85490196943283081, 0.85490196943283081), (0.95798319578170776, 0.86666667461395264, 0.86666667461395264), (0.9621848464012146, 0.87450981140136719, 0.87450981140136719), (0.96638655662536621, 0.88627451658248901, 0.88627451658248901), (0.97058820724487305, 0.89803922176361084, 0.89803922176361084), (0.97478991746902466, 0.90980392694473267, 0.90980392694473267), (0.97899156808853149, 0.92156863212585449, 0.92156863212585449), (0.98319327831268311, 0.93333333730697632, 0.93333333730697632), (0.98739492893218994, 0.94509804248809814, 0.94509804248809814), (0.99159663915634155, 0.95686274766921997, 0.95686274766921997), (0.99579828977584839, 0.97254902124404907, 0.97254902124404907), (1.0, 0.9843137264251709, 0.9843137264251709)], blue = [(0.0, 0.0, 0.0), (0.0042016808874905109, 0.18039216101169586, 0.18039216101169586), (0.0084033617749810219, 0.22745098173618317, 0.22745098173618317), (0.012605042196810246, 0.27058824896812439, 0.27058824896812439), (0.016806723549962044, 0.31764706969261169, 0.31764706969261169), (0.021008403971791267, 0.36078432202339172, 0.36078432202339172), (0.025210084393620491, 0.40784314274787903, 0.40784314274787903), (0.029411764815449715, 0.45490196347236633, 0.45490196347236633), (0.033613447099924088, 0.45490196347236633, 0.45490196347236633), (0.037815127521753311, 0.45490196347236633, 0.45490196347236633), (0.042016807943582535, 0.45490196347236633, 0.45490196347236633), (0.046218488365411758, 0.45490196347236633, 0.45490196347236633), (0.050420168787240982, 0.45882353186607361, 0.45882353186607361), (0.054621849209070206, 0.45882353186607361, 0.45882353186607361), (0.058823529630899429, 0.45882353186607361, 0.45882353186607361), (0.063025213778018951, 0.45882353186607361, 0.45882353186607361), (0.067226894199848175, 0.45882353186607361, 0.45882353186607361), (0.071428574621677399, 0.46274510025978088, 0.46274510025978088), (0.075630255043506622, 0.46274510025978088, 0.46274510025978088), (0.079831935465335846, 0.46274510025978088, 0.46274510025978088), (0.08403361588716507, 0.46274510025978088, 0.46274510025978088), (0.088235296308994293, 0.46274510025978088, 0.46274510025978088), (0.092436976730823517, 0.46666666865348816, 0.46666666865348816), (0.09663865715265274, 0.46666666865348816, 0.46666666865348816), (0.10084033757448196, 0.46666666865348816, 0.46666666865348816), (0.10504201799631119, 0.46666666865348816, 0.46666666865348816), (0.10924369841814041, 0.46666666865348816, 0.46666666865348816), (0.11344537883996964, 0.47058823704719543, 0.47058823704719543), (0.11764705926179886, 0.47058823704719543, 0.47058823704719543), (0.12184873968362808, 0.47058823704719543, 0.47058823704719543), (0.1260504275560379, 0.47058823704719543, 0.47058823704719543), (0.13025210797786713, 0.47058823704719543, 0.47058823704719543), (0.13445378839969635, 0.47450980544090271, 0.47450980544090271), (0.13865546882152557, 0.47450980544090271, 0.47450980544090271), (0.1428571492433548, 0.47450980544090271, 0.47450980544090271), (0.14705882966518402, 0.47450980544090271, 0.47450980544090271), (0.15126051008701324, 0.47450980544090271, 0.47450980544090271), (0.15546219050884247, 0.47843137383460999, 0.47843137383460999), (0.15966387093067169, 0.47843137383460999, 0.47843137383460999), (0.16386555135250092, 0.47843137383460999, 0.47843137383460999), (0.16806723177433014, 0.47843137383460999, 0.47843137383460999), (0.17226891219615936, 0.47843137383460999, 0.47843137383460999), (0.17647059261798859, 0.48235294222831726, 0.48235294222831726), (0.18067227303981781, 0.48235294222831726, 0.48235294222831726), (0.18487395346164703, 0.48235294222831726, 0.48235294222831726), (0.18907563388347626, 0.48235294222831726, 0.48235294222831726), (0.19327731430530548, 0.48235294222831726, 0.48235294222831726), (0.1974789947271347, 0.48627451062202454, 0.48627451062202454), (0.20168067514896393, 0.48627451062202454, 0.48627451062202454), (0.20588235557079315, 0.48627451062202454, 0.48627451062202454), (0.21008403599262238, 0.48627451062202454, 0.48627451062202454), (0.2142857164144516, 0.48627451062202454, 0.48627451062202454), (0.21848739683628082, 0.49019607901573181, 0.49019607901573181), (0.22268907725811005, 0.49019607901573181, 0.49019607901573181), (0.22689075767993927, 0.49019607901573181, 0.49019607901573181), (0.23109243810176849, 0.49019607901573181, 0.49019607901573181), (0.23529411852359772, 0.49019607901573181, 0.49019607901573181), (0.23949579894542694, 0.49411764740943909, 0.49411764740943909), (0.24369747936725616, 0.49411764740943909, 0.49411764740943909), (0.24789915978908539, 0.49411764740943909, 0.49411764740943909), (0.25210085511207581, 0.49411764740943909, 0.49411764740943909), (0.25630253553390503, 0.49411764740943909, 0.49411764740943909), (0.26050421595573425, 0.49803921580314636, 0.49803921580314636), (0.26470589637756348, 0.49803921580314636, 0.49803921580314636), (0.2689075767993927, 0.49803921580314636, 0.49803921580314636), (0.27310925722122192, 0.49803921580314636, 0.49803921580314636), (0.27731093764305115, 0.49803921580314636, 0.49803921580314636), (0.28151261806488037, 0.50196081399917603, 0.50196081399917603), (0.28571429848670959, 0.49411764740943909, 0.49411764740943909), (0.28991597890853882, 0.49019607901573181, 0.49019607901573181), (0.29411765933036804, 0.48627451062202454, 0.48627451062202454), (0.29831933975219727, 0.48235294222831726, 0.48235294222831726), (0.30252102017402649, 0.47843137383460999, 0.47843137383460999), (0.30672270059585571, 0.47058823704719543, 0.47058823704719543), (0.31092438101768494, 0.46666666865348816, 0.46666666865348816), (0.31512606143951416, 0.46274510025978088, 0.46274510025978088), (0.31932774186134338, 0.45882353186607361, 0.45882353186607361), (0.32352942228317261, 0.45098039507865906, 0.45098039507865906), (0.32773110270500183, 0.44705882668495178, 0.44705882668495178), (0.33193278312683105, 0.44313725829124451, 0.44313725829124451), (0.33613446354866028, 0.43529412150382996, 0.43529412150382996), (0.3403361439704895, 0.43137255311012268, 0.43137255311012268), (0.34453782439231873, 0.42745098471641541, 0.42745098471641541), (0.34873950481414795, 0.42352941632270813, 0.42352941632270813), (0.35294118523597717, 0.41568627953529358, 0.41568627953529358), (0.3571428656578064, 0.4117647111415863, 0.4117647111415863), (0.36134454607963562, 0.40784314274787903, 0.40784314274787903), (0.36554622650146484, 0.40000000596046448, 0.40000000596046448), (0.36974790692329407, 0.3960784375667572, 0.3960784375667572), (0.37394958734512329, 0.39215686917304993, 0.39215686917304993), (0.37815126776695251, 0.38431373238563538, 0.38431373238563538), (0.38235294818878174, 0.3803921639919281, 0.3803921639919281), (0.38655462861061096, 0.37647059559822083, 0.37647059559822083), (0.39075630903244019, 0.36862745881080627, 0.36862745881080627), (0.39495798945426941, 0.364705890417099, 0.364705890417099), (0.39915966987609863, 0.36078432202339172, 0.36078432202339172), (0.40336135029792786, 0.35294118523597717, 0.35294118523597717), (0.40756303071975708, 0.3490196168422699, 0.3490196168422699), (0.4117647111415863, 0.34509804844856262, 0.34509804844856262), (0.41596639156341553, 0.33725491166114807, 0.33725491166114807), (0.42016807198524475, 0.3333333432674408, 0.3333333432674408), (0.42436975240707397, 0.32941177487373352, 0.32941177487373352), (0.4285714328289032, 0.32156863808631897, 0.32156863808631897), (0.43277311325073242, 0.31764706969261169, 0.31764706969261169), (0.43697479367256165, 0.31372550129890442, 0.31372550129890442), (0.44117647409439087, 0.30588236451148987, 0.30588236451148987), (0.44537815451622009, 0.30196079611778259, 0.30196079611778259), (0.44957983493804932, 0.29803922772407532, 0.29803922772407532), (0.45378151535987854, 0.29019609093666077, 0.29019609093666077), (0.45798319578170776, 0.28627452254295349, 0.28627452254295349), (0.46218487620353699, 0.27843138575553894, 0.27843138575553894), (0.46638655662536621, 0.27450981736183167, 0.27450981736183167), (0.47058823704719543, 0.27843138575553894, 0.27843138575553894), (0.47478991746902466, 0.28235295414924622, 0.28235295414924622), (0.47899159789085388, 0.28235295414924622, 0.28235295414924622), (0.48319327831268311, 0.28627452254295349, 0.28627452254295349), (0.48739495873451233, 0.28627452254295349, 0.28627452254295349), (0.49159663915634155, 0.29019609093666077, 0.29019609093666077), (0.49579831957817078, 0.29411765933036804, 0.29411765933036804), (0.5, 0.29411765933036804, 0.29411765933036804), (0.50420171022415161, 0.29803922772407532, 0.29803922772407532), (0.50840336084365845, 0.29803922772407532, 0.29803922772407532), (0.51260507106781006, 0.30196079611778259, 0.30196079611778259), (0.51680672168731689, 0.30196079611778259, 0.30196079611778259), (0.52100843191146851, 0.30588236451148987, 0.30588236451148987), (0.52521008253097534, 0.30980393290519714, 0.30980393290519714), (0.52941179275512695, 0.30980393290519714, 0.30980393290519714), (0.53361344337463379, 0.31372550129890442, 0.31372550129890442), (0.5378151535987854, 0.31372550129890442, 0.31372550129890442), (0.54201680421829224, 0.31764706969261169, 0.31764706969261169), (0.54621851444244385, 0.32156863808631897, 0.32156863808631897), (0.55042016506195068, 0.32156863808631897, 0.32156863808631897), (0.55462187528610229, 0.32156863808631897, 0.32156863808631897), (0.55882352590560913, 0.32549020648002625, 0.32549020648002625), (0.56302523612976074, 0.32549020648002625, 0.32549020648002625), (0.56722688674926758, 0.32549020648002625, 0.32549020648002625), (0.57142859697341919, 0.32941177487373352, 0.32941177487373352), (0.57563024759292603, 0.32941177487373352, 0.32941177487373352), (0.57983195781707764, 0.32941177487373352, 0.32941177487373352), (0.58403360843658447, 0.3333333432674408, 0.3333333432674408), (0.58823531866073608, 0.3333333432674408, 0.3333333432674408), (0.59243696928024292, 0.3333333432674408, 0.3333333432674408), (0.59663867950439453, 0.33725491166114807, 0.33725491166114807), (0.60084033012390137, 0.33725491166114807, 0.33725491166114807), (0.60504204034805298, 0.33725491166114807, 0.33725491166114807), (0.60924369096755981, 0.34117648005485535, 0.34117648005485535), (0.61344540119171143, 0.34117648005485535, 0.34117648005485535), (0.61764705181121826, 0.34117648005485535, 0.34117648005485535), (0.62184876203536987, 0.34509804844856262, 0.34509804844856262), (0.62605041265487671, 0.34509804844856262, 0.34509804844856262), (0.63025212287902832, 0.34509804844856262, 0.34509804844856262), (0.63445377349853516, 0.3490196168422699, 0.3490196168422699), (0.63865548372268677, 0.3490196168422699, 0.3490196168422699), (0.6428571343421936, 0.3490196168422699, 0.3490196168422699), (0.64705884456634521, 0.35294118523597717, 0.35294118523597717), (0.65126049518585205, 0.35294118523597717, 0.35294118523597717), (0.65546220541000366, 0.35294118523597717, 0.35294118523597717), (0.6596638560295105, 0.35686275362968445, 0.35686275362968445), (0.66386556625366211, 0.35686275362968445, 0.35686275362968445), (0.66806721687316895, 0.35686275362968445, 0.35686275362968445), (0.67226892709732056, 0.36078432202339172, 0.36078432202339172), (0.67647057771682739, 0.36078432202339172, 0.36078432202339172), (0.680672287940979, 0.36078432202339172, 0.36078432202339172), (0.68487393856048584, 0.364705890417099, 0.364705890417099), (0.68907564878463745, 0.364705890417099, 0.364705890417099), (0.69327729940414429, 0.364705890417099, 0.364705890417099), (0.6974790096282959, 0.36862745881080627, 0.36862745881080627), (0.70168066024780273, 0.36862745881080627, 0.36862745881080627), (0.70588237047195435, 0.36862745881080627, 0.36862745881080627), (0.71008402109146118, 0.37254902720451355, 0.37254902720451355), (0.71428573131561279, 0.37254902720451355, 0.37254902720451355), (0.71848738193511963, 0.37254902720451355, 0.37254902720451355), (0.72268909215927124, 0.37647059559822083, 0.37647059559822083), (0.72689074277877808, 0.37647059559822083, 0.37647059559822083), (0.73109245300292969, 0.3803921639919281, 0.3803921639919281), (0.73529410362243652, 0.3803921639919281, 0.3803921639919281), (0.73949581384658813, 0.3803921639919281, 0.3803921639919281), (0.74369746446609497, 0.38431373238563538, 0.38431373238563538), (0.74789917469024658, 0.38431373238563538, 0.38431373238563538), (0.75210082530975342, 0.38431373238563538, 0.38431373238563538), (0.75630253553390503, 0.38823530077934265, 0.38823530077934265), (0.76050418615341187, 0.38823530077934265, 0.38823530077934265), (0.76470589637756348, 0.38823530077934265, 0.38823530077934265), (0.76890754699707031, 0.39215686917304993, 0.39215686917304993), (0.77310925722122192, 0.39215686917304993, 0.39215686917304993), (0.77731090784072876, 0.39215686917304993, 0.39215686917304993), (0.78151261806488037, 0.3960784375667572, 0.3960784375667572), (0.78571426868438721, 0.3960784375667572, 0.3960784375667572), (0.78991597890853882, 0.40784314274787903, 0.40784314274787903), (0.79411762952804565, 0.41568627953529358, 0.41568627953529358), (0.79831933975219727, 0.42352941632270813, 0.42352941632270813), (0.8025209903717041, 0.43529412150382996, 0.43529412150382996), (0.80672270059585571, 0.44313725829124451, 0.44313725829124451), (0.81092435121536255, 0.45490196347236633, 0.45490196347236633), (0.81512606143951416, 0.46274510025978088, 0.46274510025978088), (0.819327712059021, 0.47450980544090271, 0.47450980544090271), (0.82352942228317261, 0.48235294222831726, 0.48235294222831726), (0.82773107290267944, 0.49411764740943909, 0.49411764740943909), (0.83193278312683105, 0.5058823823928833, 0.5058823823928833), (0.83613443374633789, 0.51372551918029785, 0.51372551918029785), (0.8403361439704895, 0.52549022436141968, 0.52549022436141968), (0.84453779458999634, 0.5372549295425415, 0.5372549295425415), (0.84873950481414795, 0.54509806632995605, 0.54509806632995605), (0.85294115543365479, 0.55686277151107788, 0.55686277151107788), (0.8571428656578064, 0.56862747669219971, 0.56862747669219971), (0.86134451627731323, 0.58039218187332153, 0.58039218187332153), (0.86554622650146484, 0.58823531866073608, 0.58823531866073608), (0.86974787712097168, 0.60000002384185791, 0.60000002384185791), (0.87394958734512329, 0.61176472902297974, 0.61176472902297974), (0.87815123796463013, 0.62352943420410156, 0.62352943420410156), (0.88235294818878174, 0.63529413938522339, 0.63529413938522339), (0.88655459880828857, 0.64705884456634521, 0.64705884456634521), (0.89075630903244019, 0.65882354974746704, 0.65882354974746704), (0.89495795965194702, 0.66666668653488159, 0.66666668653488159), (0.89915966987609863, 0.67843139171600342, 0.67843139171600342), (0.90336132049560547, 0.69019609689712524, 0.69019609689712524), (0.90756303071975708, 0.70196080207824707, 0.70196080207824707), (0.91176468133926392, 0.7137255072593689, 0.7137255072593689), (0.91596639156341553, 0.72549021244049072, 0.72549021244049072), (0.92016804218292236, 0.74117648601531982, 0.74117648601531982), (0.92436975240707397, 0.75294119119644165, 0.75294119119644165), (0.92857140302658081, 0.76470589637756348, 0.76470589637756348), (0.93277311325073242, 0.7764706015586853, 0.7764706015586853), (0.93697476387023926, 0.78823530673980713, 0.78823530673980713), (0.94117647409439087, 0.80000001192092896, 0.80000001192092896), (0.94537812471389771, 0.81176471710205078, 0.81176471710205078), (0.94957983493804932, 0.82745099067687988, 0.82745099067687988), (0.95378148555755615, 0.83921569585800171, 0.83921569585800171), (0.95798319578170776, 0.85098040103912354, 0.85098040103912354), (0.9621848464012146, 0.86274510622024536, 0.86274510622024536), (0.96638655662536621, 0.87843137979507446, 0.87843137979507446), (0.97058820724487305, 0.89019608497619629, 0.89019608497619629), (0.97478991746902466, 0.90196079015731812, 0.90196079015731812), (0.97899156808853149, 0.91764706373214722, 0.91764706373214722), (0.98319327831268311, 0.92941176891326904, 0.92941176891326904), (0.98739492893218994, 0.94509804248809814, 0.94509804248809814), (0.99159663915634155, 0.95686274766921997, 0.95686274766921997), (0.99579828977584839, 0.97254902124404907, 0.97254902124404907), (1.0, 0.9843137264251709, 0.9843137264251709)], ) return ColorMapper.from_segment_map(_data, range=range, **traits) def gist_gray(range, **traits): """ Generator for the 'gist_gray' colormap from GIST. """ _data = dict( red = [(0.0, 0.0, 0.0), (0.0042016808874905109, 0.0039215688593685627, 0.0039215688593685627), (0.0084033617749810219, 0.0078431377187371254, 0.0078431377187371254), (0.012605042196810246, 0.011764706112444401, 0.011764706112444401), (0.016806723549962044, 0.015686275437474251, 0.015686275437474251), (0.021008403971791267, 0.019607843831181526, 0.019607843831181526), (0.025210084393620491, 0.023529412224888802, 0.023529412224888802), (0.029411764815449715, 0.027450980618596077, 0.027450980618596077), (0.033613447099924088, 0.035294119268655777, 0.035294119268655777), (0.037815127521753311, 0.039215687662363052, 0.039215687662363052), (0.042016807943582535, 0.043137256056070328, 0.043137256056070328), (0.046218488365411758, 0.047058824449777603, 0.047058824449777603), (0.050420168787240982, 0.050980392843484879, 0.050980392843484879), (0.054621849209070206, 0.054901961237192154, 0.054901961237192154), (0.058823529630899429, 0.058823529630899429, 0.058823529630899429), (0.063025213778018951, 0.062745101749897003, 0.062745101749897003), (0.067226894199848175, 0.066666670143604279, 0.066666670143604279), (0.071428574621677399, 0.070588238537311554, 0.070588238537311554), (0.075630255043506622, 0.074509806931018829, 0.074509806931018829), (0.079831935465335846, 0.078431375324726105, 0.078431375324726105), (0.08403361588716507, 0.08235294371843338, 0.08235294371843338), (0.088235296308994293, 0.086274512112140656, 0.086274512112140656), (0.092436976730823517, 0.090196080505847931, 0.090196080505847931), (0.09663865715265274, 0.098039217293262482, 0.098039217293262482), (0.10084033757448196, 0.10196078568696976, 0.10196078568696976), (0.10504201799631119, 0.10588235408067703, 0.10588235408067703), (0.10924369841814041, 0.10980392247438431, 0.10980392247438431), (0.11344537883996964, 0.11372549086809158, 0.11372549086809158), (0.11764705926179886, 0.11764705926179886, 0.11764705926179886), (0.12184873968362808, 0.12156862765550613, 0.12156862765550613), (0.1260504275560379, 0.12549020349979401, 0.12549020349979401), (0.13025210797786713, 0.12941177189350128, 0.12941177189350128), (0.13445378839969635, 0.13333334028720856, 0.13333334028720856), (0.13865546882152557, 0.13725490868091583, 0.13725490868091583), (0.1428571492433548, 0.14117647707462311, 0.14117647707462311), (0.14705882966518402, 0.14509804546833038, 0.14509804546833038), (0.15126051008701324, 0.14901961386203766, 0.14901961386203766), (0.15546219050884247, 0.15294118225574493, 0.15294118225574493), (0.15966387093067169, 0.16078431904315948, 0.16078431904315948), (0.16386555135250092, 0.16470588743686676, 0.16470588743686676), (0.16806723177433014, 0.16862745583057404, 0.16862745583057404), (0.17226891219615936, 0.17254902422428131, 0.17254902422428131), (0.17647059261798859, 0.17647059261798859, 0.17647059261798859), (0.18067227303981781, 0.18039216101169586, 0.18039216101169586), (0.18487395346164703, 0.18431372940540314, 0.18431372940540314), (0.18907563388347626, 0.18823529779911041, 0.18823529779911041), (0.19327731430530548, 0.19215686619281769, 0.19215686619281769), (0.1974789947271347, 0.19607843458652496, 0.19607843458652496), (0.20168067514896393, 0.20000000298023224, 0.20000000298023224), (0.20588235557079315, 0.20392157137393951, 0.20392157137393951), (0.21008403599262238, 0.20784313976764679, 0.20784313976764679), (0.2142857164144516, 0.21176470816135406, 0.21176470816135406), (0.21848739683628082, 0.21568627655506134, 0.21568627655506134), (0.22268907725811005, 0.22352941334247589, 0.22352941334247589), (0.22689075767993927, 0.22745098173618317, 0.22745098173618317), (0.23109243810176849, 0.23137255012989044, 0.23137255012989044), (0.23529411852359772, 0.23529411852359772, 0.23529411852359772), (0.23949579894542694, 0.23921568691730499, 0.23921568691730499), (0.24369747936725616, 0.24313725531101227, 0.24313725531101227), (0.24789915978908539, 0.24705882370471954, 0.24705882370471954), (0.25210085511207581, 0.25098040699958801, 0.25098040699958801), (0.25630253553390503, 0.25490197539329529, 0.25490197539329529), (0.26050421595573425, 0.25882354378700256, 0.25882354378700256), (0.26470589637756348, 0.26274511218070984, 0.26274511218070984), (0.2689075767993927, 0.26666668057441711, 0.26666668057441711), (0.27310925722122192, 0.27058824896812439, 0.27058824896812439), (0.27731093764305115, 0.27450981736183167, 0.27450981736183167), (0.28151261806488037, 0.27843138575553894, 0.27843138575553894), (0.28571429848670959, 0.28627452254295349, 0.28627452254295349), (0.28991597890853882, 0.29019609093666077, 0.29019609093666077), (0.29411765933036804, 0.29411765933036804, 0.29411765933036804), (0.29831933975219727, 0.29803922772407532, 0.29803922772407532), (0.30252102017402649, 0.30196079611778259, 0.30196079611778259), (0.30672270059585571, 0.30588236451148987, 0.30588236451148987), (0.31092438101768494, 0.30980393290519714, 0.30980393290519714), (0.31512606143951416, 0.31372550129890442, 0.31372550129890442), (0.31932774186134338, 0.31764706969261169, 0.31764706969261169), (0.32352942228317261, 0.32156863808631897, 0.32156863808631897), (0.32773110270500183, 0.32549020648002625, 0.32549020648002625), (0.33193278312683105, 0.32941177487373352, 0.32941177487373352), (0.33613446354866028, 0.3333333432674408, 0.3333333432674408), (0.3403361439704895, 0.33725491166114807, 0.33725491166114807), (0.34453782439231873, 0.34117648005485535, 0.34117648005485535), (0.34873950481414795, 0.3490196168422699, 0.3490196168422699), (0.35294118523597717, 0.35294118523597717, 0.35294118523597717), (0.3571428656578064, 0.35686275362968445, 0.35686275362968445), (0.36134454607963562, 0.36078432202339172, 0.36078432202339172), (0.36554622650146484, 0.364705890417099, 0.364705890417099), (0.36974790692329407, 0.36862745881080627, 0.36862745881080627), (0.37394958734512329, 0.37254902720451355, 0.37254902720451355), (0.37815126776695251, 0.37647059559822083, 0.37647059559822083), (0.38235294818878174, 0.3803921639919281, 0.3803921639919281), (0.38655462861061096, 0.38431373238563538, 0.38431373238563538), (0.39075630903244019, 0.38823530077934265, 0.38823530077934265), (0.39495798945426941, 0.39215686917304993, 0.39215686917304993), (0.39915966987609863, 0.3960784375667572, 0.3960784375667572), (0.40336135029792786, 0.40000000596046448, 0.40000000596046448), (0.40756303071975708, 0.40392157435417175, 0.40392157435417175), (0.4117647111415863, 0.4117647111415863, 0.4117647111415863), (0.41596639156341553, 0.41568627953529358, 0.41568627953529358), (0.42016807198524475, 0.41960784792900085, 0.41960784792900085), (0.42436975240707397, 0.42352941632270813, 0.42352941632270813), (0.4285714328289032, 0.42745098471641541, 0.42745098471641541), (0.43277311325073242, 0.43137255311012268, 0.43137255311012268), (0.43697479367256165, 0.43529412150382996, 0.43529412150382996), (0.44117647409439087, 0.43921568989753723, 0.43921568989753723), (0.44537815451622009, 0.44313725829124451, 0.44313725829124451), (0.44957983493804932, 0.44705882668495178, 0.44705882668495178), (0.45378151535987854, 0.45098039507865906, 0.45098039507865906), (0.45798319578170776, 0.45490196347236633, 0.45490196347236633), (0.46218487620353699, 0.45882353186607361, 0.45882353186607361), (0.46638655662536621, 0.46274510025978088, 0.46274510025978088), (0.47058823704719543, 0.46666666865348816, 0.46666666865348816), (0.47478991746902466, 0.47450980544090271, 0.47450980544090271), (0.47899159789085388, 0.47843137383460999, 0.47843137383460999), (0.48319327831268311, 0.48235294222831726, 0.48235294222831726), (0.48739495873451233, 0.48627451062202454, 0.48627451062202454), (0.49159663915634155, 0.49019607901573181, 0.49019607901573181), (0.49579831957817078, 0.49411764740943909, 0.49411764740943909), (0.5, 0.49803921580314636, 0.49803921580314636), (0.50420171022415161, 0.50196081399917603, 0.50196081399917603), (0.50840336084365845, 0.5058823823928833, 0.5058823823928833), (0.51260507106781006, 0.50980395078659058, 0.50980395078659058), (0.51680672168731689, 0.51372551918029785, 0.51372551918029785), (0.52100843191146851, 0.51764708757400513, 0.51764708757400513), (0.52521008253097534, 0.5215686559677124, 0.5215686559677124), (0.52941179275512695, 0.52549022436141968, 0.52549022436141968), (0.53361344337463379, 0.52941179275512695, 0.52941179275512695), (0.5378151535987854, 0.5372549295425415, 0.5372549295425415), (0.54201680421829224, 0.54117649793624878, 0.54117649793624878), (0.54621851444244385, 0.54509806632995605, 0.54509806632995605), (0.55042016506195068, 0.54901963472366333, 0.54901963472366333), (0.55462187528610229, 0.55294120311737061, 0.55294120311737061), (0.55882352590560913, 0.55686277151107788, 0.55686277151107788), (0.56302523612976074, 0.56078433990478516, 0.56078433990478516), (0.56722688674926758, 0.56470590829849243, 0.56470590829849243), (0.57142859697341919, 0.56862747669219971, 0.56862747669219971), (0.57563024759292603, 0.57254904508590698, 0.57254904508590698), (0.57983195781707764, 0.57647061347961426, 0.57647061347961426), (0.58403360843658447, 0.58039218187332153, 0.58039218187332153), (0.58823531866073608, 0.58431375026702881, 0.58431375026702881), (0.59243696928024292, 0.58823531866073608, 0.58823531866073608), (0.59663867950439453, 0.59215688705444336, 0.59215688705444336), (0.60084033012390137, 0.60000002384185791, 0.60000002384185791), (0.60504204034805298, 0.60392159223556519, 0.60392159223556519), (0.60924369096755981, 0.60784316062927246, 0.60784316062927246), (0.61344540119171143, 0.61176472902297974, 0.61176472902297974), (0.61764705181121826, 0.61568629741668701, 0.61568629741668701), (0.62184876203536987, 0.61960786581039429, 0.61960786581039429), (0.62605041265487671, 0.62352943420410156, 0.62352943420410156), (0.63025212287902832, 0.62745100259780884, 0.62745100259780884), (0.63445377349853516, 0.63137257099151611, 0.63137257099151611), (0.63865548372268677, 0.63529413938522339, 0.63529413938522339), (0.6428571343421936, 0.63921570777893066, 0.63921570777893066), (0.64705884456634521, 0.64313727617263794, 0.64313727617263794), (0.65126049518585205, 0.64705884456634521, 0.64705884456634521), (0.65546220541000366, 0.65098041296005249, 0.65098041296005249), (0.6596638560295105, 0.65490198135375977, 0.65490198135375977), (0.66386556625366211, 0.66274511814117432, 0.66274511814117432), (0.66806721687316895, 0.66666668653488159, 0.66666668653488159), (0.67226892709732056, 0.67058825492858887, 0.67058825492858887), (0.67647057771682739, 0.67450982332229614, 0.67450982332229614), (0.680672287940979, 0.67843139171600342, 0.67843139171600342), (0.68487393856048584, 0.68235296010971069, 0.68235296010971069), (0.68907564878463745, 0.68627452850341797, 0.68627452850341797), (0.69327729940414429, 0.69019609689712524, 0.69019609689712524), (0.6974790096282959, 0.69411766529083252, 0.69411766529083252), (0.70168066024780273, 0.69803923368453979, 0.69803923368453979), (0.70588237047195435, 0.70196080207824707, 0.70196080207824707), (0.71008402109146118, 0.70588237047195435, 0.70588237047195435), (0.71428573131561279, 0.70980393886566162, 0.70980393886566162), (0.71848738193511963, 0.7137255072593689, 0.7137255072593689), (0.72268909215927124, 0.71764707565307617, 0.71764707565307617), (0.72689074277877808, 0.72549021244049072, 0.72549021244049072), (0.73109245300292969, 0.729411780834198, 0.729411780834198), (0.73529410362243652, 0.73333334922790527, 0.73333334922790527), (0.73949581384658813, 0.73725491762161255, 0.73725491762161255), (0.74369746446609497, 0.74117648601531982, 0.74117648601531982), (0.74789917469024658, 0.7450980544090271, 0.7450980544090271), (0.75210082530975342, 0.74901962280273438, 0.74901962280273438), (0.75630253553390503, 0.75294119119644165, 0.75294119119644165), (0.76050418615341187, 0.75686275959014893, 0.75686275959014893), (0.76470589637756348, 0.7607843279838562, 0.7607843279838562), (0.76890754699707031, 0.76470589637756348, 0.76470589637756348), (0.77310925722122192, 0.76862746477127075, 0.76862746477127075), (0.77731090784072876, 0.77254903316497803, 0.77254903316497803), (0.78151261806488037, 0.7764706015586853, 0.7764706015586853), (0.78571426868438721, 0.78039216995239258, 0.78039216995239258), (0.78991597890853882, 0.78823530673980713, 0.78823530673980713), (0.79411762952804565, 0.7921568751335144, 0.7921568751335144), (0.79831933975219727, 0.79607844352722168, 0.79607844352722168), (0.8025209903717041, 0.80000001192092896, 0.80000001192092896), (0.80672270059585571, 0.80392158031463623, 0.80392158031463623), (0.81092435121536255, 0.80784314870834351, 0.80784314870834351), (0.81512606143951416, 0.81176471710205078, 0.81176471710205078), (0.819327712059021, 0.81568628549575806, 0.81568628549575806), (0.82352942228317261, 0.81960785388946533, 0.81960785388946533), (0.82773107290267944, 0.82352942228317261, 0.82352942228317261), (0.83193278312683105, 0.82745099067687988, 0.82745099067687988), (0.83613443374633789, 0.83137255907058716, 0.83137255907058716), (0.8403361439704895, 0.83529412746429443, 0.83529412746429443), (0.84453779458999634, 0.83921569585800171, 0.83921569585800171), (0.84873950481414795, 0.84313726425170898, 0.84313726425170898), (0.85294115543365479, 0.85098040103912354, 0.85098040103912354), (0.8571428656578064, 0.85490196943283081, 0.85490196943283081), (0.86134451627731323, 0.85882353782653809, 0.85882353782653809), (0.86554622650146484, 0.86274510622024536, 0.86274510622024536), (0.86974787712097168, 0.86666667461395264, 0.86666667461395264), (0.87394958734512329, 0.87058824300765991, 0.87058824300765991), (0.87815123796463013, 0.87450981140136719, 0.87450981140136719), (0.88235294818878174, 0.87843137979507446, 0.87843137979507446), (0.88655459880828857, 0.88235294818878174, 0.88235294818878174), (0.89075630903244019, 0.88627451658248901, 0.88627451658248901), (0.89495795965194702, 0.89019608497619629, 0.89019608497619629), (0.89915966987609863, 0.89411765336990356, 0.89411765336990356), (0.90336132049560547, 0.89803922176361084, 0.89803922176361084), (0.90756303071975708, 0.90196079015731812, 0.90196079015731812), (0.91176468133926392, 0.90588235855102539, 0.90588235855102539), (0.91596639156341553, 0.91372549533843994, 0.91372549533843994), (0.92016804218292236, 0.91764706373214722, 0.91764706373214722), (0.92436975240707397, 0.92156863212585449, 0.92156863212585449), (0.92857140302658081, 0.92549020051956177, 0.92549020051956177), (0.93277311325073242, 0.92941176891326904, 0.92941176891326904), (0.93697476387023926, 0.93333333730697632, 0.93333333730697632), (0.94117647409439087, 0.93725490570068359, 0.93725490570068359), (0.94537812471389771, 0.94117647409439087, 0.94117647409439087), (0.94957983493804932, 0.94509804248809814, 0.94509804248809814), (0.95378148555755615, 0.94901961088180542, 0.94901961088180542), (0.95798319578170776, 0.9529411792755127, 0.9529411792755127), (0.9621848464012146, 0.95686274766921997, 0.95686274766921997), (0.96638655662536621, 0.96078431606292725, 0.96078431606292725), (0.97058820724487305, 0.96470588445663452, 0.96470588445663452), (0.97478991746902466, 0.9686274528503418, 0.9686274528503418), (0.97899156808853149, 0.97647058963775635, 0.97647058963775635), (0.98319327831268311, 0.98039215803146362, 0.98039215803146362), (0.98739492893218994, 0.9843137264251709, 0.9843137264251709), (0.99159663915634155, 0.98823529481887817, 0.98823529481887817), (0.99579828977584839, 0.99215686321258545, 0.99215686321258545), (1.0, 0.99607843160629272, 0.99607843160629272)], green = [(0.0, 0.0, 0.0), (0.0042016808874905109, 0.0039215688593685627, 0.0039215688593685627), (0.0084033617749810219, 0.0078431377187371254, 0.0078431377187371254), (0.012605042196810246, 0.011764706112444401, 0.011764706112444401), (0.016806723549962044, 0.015686275437474251, 0.015686275437474251), (0.021008403971791267, 0.019607843831181526, 0.019607843831181526), (0.025210084393620491, 0.023529412224888802, 0.023529412224888802), (0.029411764815449715, 0.027450980618596077, 0.027450980618596077), (0.033613447099924088, 0.035294119268655777, 0.035294119268655777), (0.037815127521753311, 0.039215687662363052, 0.039215687662363052), (0.042016807943582535, 0.043137256056070328, 0.043137256056070328), (0.046218488365411758, 0.047058824449777603, 0.047058824449777603), (0.050420168787240982, 0.050980392843484879, 0.050980392843484879), (0.054621849209070206, 0.054901961237192154, 0.054901961237192154), (0.058823529630899429, 0.058823529630899429, 0.058823529630899429), (0.063025213778018951, 0.062745101749897003, 0.062745101749897003), (0.067226894199848175, 0.066666670143604279, 0.066666670143604279), (0.071428574621677399, 0.070588238537311554, 0.070588238537311554), (0.075630255043506622, 0.074509806931018829, 0.074509806931018829), (0.079831935465335846, 0.078431375324726105, 0.078431375324726105), (0.08403361588716507, 0.08235294371843338, 0.08235294371843338), (0.088235296308994293, 0.086274512112140656, 0.086274512112140656), (0.092436976730823517, 0.090196080505847931, 0.090196080505847931), (0.09663865715265274, 0.098039217293262482, 0.098039217293262482), (0.10084033757448196, 0.10196078568696976, 0.10196078568696976), (0.10504201799631119, 0.10588235408067703, 0.10588235408067703), (0.10924369841814041, 0.10980392247438431, 0.10980392247438431), (0.11344537883996964, 0.11372549086809158, 0.11372549086809158), (0.11764705926179886, 0.11764705926179886, 0.11764705926179886), (0.12184873968362808, 0.12156862765550613, 0.12156862765550613), (0.1260504275560379, 0.12549020349979401, 0.12549020349979401), (0.13025210797786713, 0.12941177189350128, 0.12941177189350128), (0.13445378839969635, 0.13333334028720856, 0.13333334028720856), (0.13865546882152557, 0.13725490868091583, 0.13725490868091583), (0.1428571492433548, 0.14117647707462311, 0.14117647707462311), (0.14705882966518402, 0.14509804546833038, 0.14509804546833038), (0.15126051008701324, 0.14901961386203766, 0.14901961386203766), (0.15546219050884247, 0.15294118225574493, 0.15294118225574493), (0.15966387093067169, 0.16078431904315948, 0.16078431904315948), (0.16386555135250092, 0.16470588743686676, 0.16470588743686676), (0.16806723177433014, 0.16862745583057404, 0.16862745583057404), (0.17226891219615936, 0.17254902422428131, 0.17254902422428131), (0.17647059261798859, 0.17647059261798859, 0.17647059261798859), (0.18067227303981781, 0.18039216101169586, 0.18039216101169586), (0.18487395346164703, 0.18431372940540314, 0.18431372940540314), (0.18907563388347626, 0.18823529779911041, 0.18823529779911041), (0.19327731430530548, 0.19215686619281769, 0.19215686619281769), (0.1974789947271347, 0.19607843458652496, 0.19607843458652496), (0.20168067514896393, 0.20000000298023224, 0.20000000298023224), (0.20588235557079315, 0.20392157137393951, 0.20392157137393951), (0.21008403599262238, 0.20784313976764679, 0.20784313976764679), (0.2142857164144516, 0.21176470816135406, 0.21176470816135406), (0.21848739683628082, 0.21568627655506134, 0.21568627655506134), (0.22268907725811005, 0.22352941334247589, 0.22352941334247589), (0.22689075767993927, 0.22745098173618317, 0.22745098173618317), (0.23109243810176849, 0.23137255012989044, 0.23137255012989044), (0.23529411852359772, 0.23529411852359772, 0.23529411852359772), (0.23949579894542694, 0.23921568691730499, 0.23921568691730499), (0.24369747936725616, 0.24313725531101227, 0.24313725531101227), (0.24789915978908539, 0.24705882370471954, 0.24705882370471954), (0.25210085511207581, 0.25098040699958801, 0.25098040699958801), (0.25630253553390503, 0.25490197539329529, 0.25490197539329529), (0.26050421595573425, 0.25882354378700256, 0.25882354378700256), (0.26470589637756348, 0.26274511218070984, 0.26274511218070984), (0.2689075767993927, 0.26666668057441711, 0.26666668057441711), (0.27310925722122192, 0.27058824896812439, 0.27058824896812439), (0.27731093764305115, 0.27450981736183167, 0.27450981736183167), (0.28151261806488037, 0.27843138575553894, 0.27843138575553894), (0.28571429848670959, 0.28627452254295349, 0.28627452254295349), (0.28991597890853882, 0.29019609093666077, 0.29019609093666077), (0.29411765933036804, 0.29411765933036804, 0.29411765933036804), (0.29831933975219727, 0.29803922772407532, 0.29803922772407532), (0.30252102017402649, 0.30196079611778259, 0.30196079611778259), (0.30672270059585571, 0.30588236451148987, 0.30588236451148987), (0.31092438101768494, 0.30980393290519714, 0.30980393290519714), (0.31512606143951416, 0.31372550129890442, 0.31372550129890442), (0.31932774186134338, 0.31764706969261169, 0.31764706969261169), (0.32352942228317261, 0.32156863808631897, 0.32156863808631897), (0.32773110270500183, 0.32549020648002625, 0.32549020648002625), (0.33193278312683105, 0.32941177487373352, 0.32941177487373352), (0.33613446354866028, 0.3333333432674408, 0.3333333432674408), (0.3403361439704895, 0.33725491166114807, 0.33725491166114807), (0.34453782439231873, 0.34117648005485535, 0.34117648005485535), (0.34873950481414795, 0.3490196168422699, 0.3490196168422699), (0.35294118523597717, 0.35294118523597717, 0.35294118523597717), (0.3571428656578064, 0.35686275362968445, 0.35686275362968445), (0.36134454607963562, 0.36078432202339172, 0.36078432202339172), (0.36554622650146484, 0.364705890417099, 0.364705890417099), (0.36974790692329407, 0.36862745881080627, 0.36862745881080627), (0.37394958734512329, 0.37254902720451355, 0.37254902720451355), (0.37815126776695251, 0.37647059559822083, 0.37647059559822083), (0.38235294818878174, 0.3803921639919281, 0.3803921639919281), (0.38655462861061096, 0.38431373238563538, 0.38431373238563538), (0.39075630903244019, 0.38823530077934265, 0.38823530077934265), (0.39495798945426941, 0.39215686917304993, 0.39215686917304993), (0.39915966987609863, 0.3960784375667572, 0.3960784375667572), (0.40336135029792786, 0.40000000596046448, 0.40000000596046448), (0.40756303071975708, 0.40392157435417175, 0.40392157435417175), (0.4117647111415863, 0.4117647111415863, 0.4117647111415863), (0.41596639156341553, 0.41568627953529358, 0.41568627953529358), (0.42016807198524475, 0.41960784792900085, 0.41960784792900085), (0.42436975240707397, 0.42352941632270813, 0.42352941632270813), (0.4285714328289032, 0.42745098471641541, 0.42745098471641541), (0.43277311325073242, 0.43137255311012268, 0.43137255311012268), (0.43697479367256165, 0.43529412150382996, 0.43529412150382996), (0.44117647409439087, 0.43921568989753723, 0.43921568989753723), (0.44537815451622009, 0.44313725829124451, 0.44313725829124451), (0.44957983493804932, 0.44705882668495178, 0.44705882668495178), (0.45378151535987854, 0.45098039507865906, 0.45098039507865906), (0.45798319578170776, 0.45490196347236633, 0.45490196347236633), (0.46218487620353699, 0.45882353186607361, 0.45882353186607361), (0.46638655662536621, 0.46274510025978088, 0.46274510025978088), (0.47058823704719543, 0.46666666865348816, 0.46666666865348816), (0.47478991746902466, 0.47450980544090271, 0.47450980544090271), (0.47899159789085388, 0.47843137383460999, 0.47843137383460999), (0.48319327831268311, 0.48235294222831726, 0.48235294222831726), (0.48739495873451233, 0.48627451062202454, 0.48627451062202454), (0.49159663915634155, 0.49019607901573181, 0.49019607901573181), (0.49579831957817078, 0.49411764740943909, 0.49411764740943909), (0.5, 0.49803921580314636, 0.49803921580314636), (0.50420171022415161, 0.50196081399917603, 0.50196081399917603), (0.50840336084365845, 0.5058823823928833, 0.5058823823928833), (0.51260507106781006, 0.50980395078659058, 0.50980395078659058), (0.51680672168731689, 0.51372551918029785, 0.51372551918029785), (0.52100843191146851, 0.51764708757400513, 0.51764708757400513), (0.52521008253097534, 0.5215686559677124, 0.5215686559677124), (0.52941179275512695, 0.52549022436141968, 0.52549022436141968), (0.53361344337463379, 0.52941179275512695, 0.52941179275512695), (0.5378151535987854, 0.5372549295425415, 0.5372549295425415), (0.54201680421829224, 0.54117649793624878, 0.54117649793624878), (0.54621851444244385, 0.54509806632995605, 0.54509806632995605), (0.55042016506195068, 0.54901963472366333, 0.54901963472366333), (0.55462187528610229, 0.55294120311737061, 0.55294120311737061), (0.55882352590560913, 0.55686277151107788, 0.55686277151107788), (0.56302523612976074, 0.56078433990478516, 0.56078433990478516), (0.56722688674926758, 0.56470590829849243, 0.56470590829849243), (0.57142859697341919, 0.56862747669219971, 0.56862747669219971), (0.57563024759292603, 0.57254904508590698, 0.57254904508590698), (0.57983195781707764, 0.57647061347961426, 0.57647061347961426), (0.58403360843658447, 0.58039218187332153, 0.58039218187332153), (0.58823531866073608, 0.58431375026702881, 0.58431375026702881), (0.59243696928024292, 0.58823531866073608, 0.58823531866073608), (0.59663867950439453, 0.59215688705444336, 0.59215688705444336), (0.60084033012390137, 0.60000002384185791, 0.60000002384185791), (0.60504204034805298, 0.60392159223556519, 0.60392159223556519), (0.60924369096755981, 0.60784316062927246, 0.60784316062927246), (0.61344540119171143, 0.61176472902297974, 0.61176472902297974), (0.61764705181121826, 0.61568629741668701, 0.61568629741668701), (0.62184876203536987, 0.61960786581039429, 0.61960786581039429), (0.62605041265487671, 0.62352943420410156, 0.62352943420410156), (0.63025212287902832, 0.62745100259780884, 0.62745100259780884), (0.63445377349853516, 0.63137257099151611, 0.63137257099151611), (0.63865548372268677, 0.63529413938522339, 0.63529413938522339), (0.6428571343421936, 0.63921570777893066, 0.63921570777893066), (0.64705884456634521, 0.64313727617263794, 0.64313727617263794), (0.65126049518585205, 0.64705884456634521, 0.64705884456634521), (0.65546220541000366, 0.65098041296005249, 0.65098041296005249), (0.6596638560295105, 0.65490198135375977, 0.65490198135375977), (0.66386556625366211, 0.66274511814117432, 0.66274511814117432), (0.66806721687316895, 0.66666668653488159, 0.66666668653488159), (0.67226892709732056, 0.67058825492858887, 0.67058825492858887), (0.67647057771682739, 0.67450982332229614, 0.67450982332229614), (0.680672287940979, 0.67843139171600342, 0.67843139171600342), (0.68487393856048584, 0.68235296010971069, 0.68235296010971069), (0.68907564878463745, 0.68627452850341797, 0.68627452850341797), (0.69327729940414429, 0.69019609689712524, 0.69019609689712524), (0.6974790096282959, 0.69411766529083252, 0.69411766529083252), (0.70168066024780273, 0.69803923368453979, 0.69803923368453979), (0.70588237047195435, 0.70196080207824707, 0.70196080207824707), (0.71008402109146118, 0.70588237047195435, 0.70588237047195435), (0.71428573131561279, 0.70980393886566162, 0.70980393886566162), (0.71848738193511963, 0.7137255072593689, 0.7137255072593689), (0.72268909215927124, 0.71764707565307617, 0.71764707565307617), (0.72689074277877808, 0.72549021244049072, 0.72549021244049072), (0.73109245300292969, 0.729411780834198, 0.729411780834198), (0.73529410362243652, 0.73333334922790527, 0.73333334922790527), (0.73949581384658813, 0.73725491762161255, 0.73725491762161255), (0.74369746446609497, 0.74117648601531982, 0.74117648601531982), (0.74789917469024658, 0.7450980544090271, 0.7450980544090271), (0.75210082530975342, 0.74901962280273438, 0.74901962280273438), (0.75630253553390503, 0.75294119119644165, 0.75294119119644165), (0.76050418615341187, 0.75686275959014893, 0.75686275959014893), (0.76470589637756348, 0.7607843279838562, 0.7607843279838562), (0.76890754699707031, 0.76470589637756348, 0.76470589637756348), (0.77310925722122192, 0.76862746477127075, 0.76862746477127075), (0.77731090784072876, 0.77254903316497803, 0.77254903316497803), (0.78151261806488037, 0.7764706015586853, 0.7764706015586853), (0.78571426868438721, 0.78039216995239258, 0.78039216995239258), (0.78991597890853882, 0.78823530673980713, 0.78823530673980713), (0.79411762952804565, 0.7921568751335144, 0.7921568751335144), (0.79831933975219727, 0.79607844352722168, 0.79607844352722168), (0.8025209903717041, 0.80000001192092896, 0.80000001192092896), (0.80672270059585571, 0.80392158031463623, 0.80392158031463623), (0.81092435121536255, 0.80784314870834351, 0.80784314870834351), (0.81512606143951416, 0.81176471710205078, 0.81176471710205078), (0.819327712059021, 0.81568628549575806, 0.81568628549575806), (0.82352942228317261, 0.81960785388946533, 0.81960785388946533), (0.82773107290267944, 0.82352942228317261, 0.82352942228317261), (0.83193278312683105, 0.82745099067687988, 0.82745099067687988), (0.83613443374633789, 0.83137255907058716, 0.83137255907058716), (0.8403361439704895, 0.83529412746429443, 0.83529412746429443), (0.84453779458999634, 0.83921569585800171, 0.83921569585800171), (0.84873950481414795, 0.84313726425170898, 0.84313726425170898), (0.85294115543365479, 0.85098040103912354, 0.85098040103912354), (0.8571428656578064, 0.85490196943283081, 0.85490196943283081), (0.86134451627731323, 0.85882353782653809, 0.85882353782653809), (0.86554622650146484, 0.86274510622024536, 0.86274510622024536), (0.86974787712097168, 0.86666667461395264, 0.86666667461395264), (0.87394958734512329, 0.87058824300765991, 0.87058824300765991), (0.87815123796463013, 0.87450981140136719, 0.87450981140136719), (0.88235294818878174, 0.87843137979507446, 0.87843137979507446), (0.88655459880828857, 0.88235294818878174, 0.88235294818878174), (0.89075630903244019, 0.88627451658248901, 0.88627451658248901), (0.89495795965194702, 0.89019608497619629, 0.89019608497619629), (0.89915966987609863, 0.89411765336990356, 0.89411765336990356), (0.90336132049560547, 0.89803922176361084, 0.89803922176361084), (0.90756303071975708, 0.90196079015731812, 0.90196079015731812), (0.91176468133926392, 0.90588235855102539, 0.90588235855102539), (0.91596639156341553, 0.91372549533843994, 0.91372549533843994), (0.92016804218292236, 0.91764706373214722, 0.91764706373214722), (0.92436975240707397, 0.92156863212585449, 0.92156863212585449), (0.92857140302658081, 0.92549020051956177, 0.92549020051956177), (0.93277311325073242, 0.92941176891326904, 0.92941176891326904), (0.93697476387023926, 0.93333333730697632, 0.93333333730697632), (0.94117647409439087, 0.93725490570068359, 0.93725490570068359), (0.94537812471389771, 0.94117647409439087, 0.94117647409439087), (0.94957983493804932, 0.94509804248809814, 0.94509804248809814), (0.95378148555755615, 0.94901961088180542, 0.94901961088180542), (0.95798319578170776, 0.9529411792755127, 0.9529411792755127), (0.9621848464012146, 0.95686274766921997, 0.95686274766921997), (0.96638655662536621, 0.96078431606292725, 0.96078431606292725), (0.97058820724487305, 0.96470588445663452, 0.96470588445663452), (0.97478991746902466, 0.9686274528503418, 0.9686274528503418), (0.97899156808853149, 0.97647058963775635, 0.97647058963775635), (0.98319327831268311, 0.98039215803146362, 0.98039215803146362), (0.98739492893218994, 0.9843137264251709, 0.9843137264251709), (0.99159663915634155, 0.98823529481887817, 0.98823529481887817), (0.99579828977584839, 0.99215686321258545, 0.99215686321258545), (1.0, 0.99607843160629272, 0.99607843160629272)], blue = [(0.0, 0.0, 0.0), (0.0042016808874905109, 0.0039215688593685627, 0.0039215688593685627), (0.0084033617749810219, 0.0078431377187371254, 0.0078431377187371254), (0.012605042196810246, 0.011764706112444401, 0.011764706112444401), (0.016806723549962044, 0.015686275437474251, 0.015686275437474251), (0.021008403971791267, 0.019607843831181526, 0.019607843831181526), (0.025210084393620491, 0.023529412224888802, 0.023529412224888802), (0.029411764815449715, 0.027450980618596077, 0.027450980618596077), (0.033613447099924088, 0.035294119268655777, 0.035294119268655777), (0.037815127521753311, 0.039215687662363052, 0.039215687662363052), (0.042016807943582535, 0.043137256056070328, 0.043137256056070328), (0.046218488365411758, 0.047058824449777603, 0.047058824449777603), (0.050420168787240982, 0.050980392843484879, 0.050980392843484879), (0.054621849209070206, 0.054901961237192154, 0.054901961237192154), (0.058823529630899429, 0.058823529630899429, 0.058823529630899429), (0.063025213778018951, 0.062745101749897003, 0.062745101749897003), (0.067226894199848175, 0.066666670143604279, 0.066666670143604279), (0.071428574621677399, 0.070588238537311554, 0.070588238537311554), (0.075630255043506622, 0.074509806931018829, 0.074509806931018829), (0.079831935465335846, 0.078431375324726105, 0.078431375324726105), (0.08403361588716507, 0.08235294371843338, 0.08235294371843338), (0.088235296308994293, 0.086274512112140656, 0.086274512112140656), (0.092436976730823517, 0.090196080505847931, 0.090196080505847931), (0.09663865715265274, 0.098039217293262482, 0.098039217293262482), (0.10084033757448196, 0.10196078568696976, 0.10196078568696976), (0.10504201799631119, 0.10588235408067703, 0.10588235408067703), (0.10924369841814041, 0.10980392247438431, 0.10980392247438431), (0.11344537883996964, 0.11372549086809158, 0.11372549086809158), (0.11764705926179886, 0.11764705926179886, 0.11764705926179886), (0.12184873968362808, 0.12156862765550613, 0.12156862765550613), (0.1260504275560379, 0.12549020349979401, 0.12549020349979401), (0.13025210797786713, 0.12941177189350128, 0.12941177189350128), (0.13445378839969635, 0.13333334028720856, 0.13333334028720856), (0.13865546882152557, 0.13725490868091583, 0.13725490868091583), (0.1428571492433548, 0.14117647707462311, 0.14117647707462311), (0.14705882966518402, 0.14509804546833038, 0.14509804546833038), (0.15126051008701324, 0.14901961386203766, 0.14901961386203766), (0.15546219050884247, 0.15294118225574493, 0.15294118225574493), (0.15966387093067169, 0.16078431904315948, 0.16078431904315948), (0.16386555135250092, 0.16470588743686676, 0.16470588743686676), (0.16806723177433014, 0.16862745583057404, 0.16862745583057404), (0.17226891219615936, 0.17254902422428131, 0.17254902422428131), (0.17647059261798859, 0.17647059261798859, 0.17647059261798859), (0.18067227303981781, 0.18039216101169586, 0.18039216101169586), (0.18487395346164703, 0.18431372940540314, 0.18431372940540314), (0.18907563388347626, 0.18823529779911041, 0.18823529779911041), (0.19327731430530548, 0.19215686619281769, 0.19215686619281769), (0.1974789947271347, 0.19607843458652496, 0.19607843458652496), (0.20168067514896393, 0.20000000298023224, 0.20000000298023224), (0.20588235557079315, 0.20392157137393951, 0.20392157137393951), (0.21008403599262238, 0.20784313976764679, 0.20784313976764679), (0.2142857164144516, 0.21176470816135406, 0.21176470816135406), (0.21848739683628082, 0.21568627655506134, 0.21568627655506134), (0.22268907725811005, 0.22352941334247589, 0.22352941334247589), (0.22689075767993927, 0.22745098173618317, 0.22745098173618317), (0.23109243810176849, 0.23137255012989044, 0.23137255012989044), (0.23529411852359772, 0.23529411852359772, 0.23529411852359772), (0.23949579894542694, 0.23921568691730499, 0.23921568691730499), (0.24369747936725616, 0.24313725531101227, 0.24313725531101227), (0.24789915978908539, 0.24705882370471954, 0.24705882370471954), (0.25210085511207581, 0.25098040699958801, 0.25098040699958801), (0.25630253553390503, 0.25490197539329529, 0.25490197539329529), (0.26050421595573425, 0.25882354378700256, 0.25882354378700256), (0.26470589637756348, 0.26274511218070984, 0.26274511218070984), (0.2689075767993927, 0.26666668057441711, 0.26666668057441711), (0.27310925722122192, 0.27058824896812439, 0.27058824896812439), (0.27731093764305115, 0.27450981736183167, 0.27450981736183167), (0.28151261806488037, 0.27843138575553894, 0.27843138575553894), (0.28571429848670959, 0.28627452254295349, 0.28627452254295349), (0.28991597890853882, 0.29019609093666077, 0.29019609093666077), (0.29411765933036804, 0.29411765933036804, 0.29411765933036804), (0.29831933975219727, 0.29803922772407532, 0.29803922772407532), (0.30252102017402649, 0.30196079611778259, 0.30196079611778259), (0.30672270059585571, 0.30588236451148987, 0.30588236451148987), (0.31092438101768494, 0.30980393290519714, 0.30980393290519714), (0.31512606143951416, 0.31372550129890442, 0.31372550129890442), (0.31932774186134338, 0.31764706969261169, 0.31764706969261169), (0.32352942228317261, 0.32156863808631897, 0.32156863808631897), (0.32773110270500183, 0.32549020648002625, 0.32549020648002625), (0.33193278312683105, 0.32941177487373352, 0.32941177487373352), (0.33613446354866028, 0.3333333432674408, 0.3333333432674408), (0.3403361439704895, 0.33725491166114807, 0.33725491166114807), (0.34453782439231873, 0.34117648005485535, 0.34117648005485535), (0.34873950481414795, 0.3490196168422699, 0.3490196168422699), (0.35294118523597717, 0.35294118523597717, 0.35294118523597717), (0.3571428656578064, 0.35686275362968445, 0.35686275362968445), (0.36134454607963562, 0.36078432202339172, 0.36078432202339172), (0.36554622650146484, 0.364705890417099, 0.364705890417099), (0.36974790692329407, 0.36862745881080627, 0.36862745881080627), (0.37394958734512329, 0.37254902720451355, 0.37254902720451355), (0.37815126776695251, 0.37647059559822083, 0.37647059559822083), (0.38235294818878174, 0.3803921639919281, 0.3803921639919281), (0.38655462861061096, 0.38431373238563538, 0.38431373238563538), (0.39075630903244019, 0.38823530077934265, 0.38823530077934265), (0.39495798945426941, 0.39215686917304993, 0.39215686917304993), (0.39915966987609863, 0.3960784375667572, 0.3960784375667572), (0.40336135029792786, 0.40000000596046448, 0.40000000596046448), (0.40756303071975708, 0.40392157435417175, 0.40392157435417175), (0.4117647111415863, 0.4117647111415863, 0.4117647111415863), (0.41596639156341553, 0.41568627953529358, 0.41568627953529358), (0.42016807198524475, 0.41960784792900085, 0.41960784792900085), (0.42436975240707397, 0.42352941632270813, 0.42352941632270813), (0.4285714328289032, 0.42745098471641541, 0.42745098471641541), (0.43277311325073242, 0.43137255311012268, 0.43137255311012268), (0.43697479367256165, 0.43529412150382996, 0.43529412150382996), (0.44117647409439087, 0.43921568989753723, 0.43921568989753723), (0.44537815451622009, 0.44313725829124451, 0.44313725829124451), (0.44957983493804932, 0.44705882668495178, 0.44705882668495178), (0.45378151535987854, 0.45098039507865906, 0.45098039507865906), (0.45798319578170776, 0.45490196347236633, 0.45490196347236633), (0.46218487620353699, 0.45882353186607361, 0.45882353186607361), (0.46638655662536621, 0.46274510025978088, 0.46274510025978088), (0.47058823704719543, 0.46666666865348816, 0.46666666865348816), (0.47478991746902466, 0.47450980544090271, 0.47450980544090271), (0.47899159789085388, 0.47843137383460999, 0.47843137383460999), (0.48319327831268311, 0.48235294222831726, 0.48235294222831726), (0.48739495873451233, 0.48627451062202454, 0.48627451062202454), (0.49159663915634155, 0.49019607901573181, 0.49019607901573181), (0.49579831957817078, 0.49411764740943909, 0.49411764740943909), (0.5, 0.49803921580314636, 0.49803921580314636), (0.50420171022415161, 0.50196081399917603, 0.50196081399917603), (0.50840336084365845, 0.5058823823928833, 0.5058823823928833), (0.51260507106781006, 0.50980395078659058, 0.50980395078659058), (0.51680672168731689, 0.51372551918029785, 0.51372551918029785), (0.52100843191146851, 0.51764708757400513, 0.51764708757400513), (0.52521008253097534, 0.5215686559677124, 0.5215686559677124), (0.52941179275512695, 0.52549022436141968, 0.52549022436141968), (0.53361344337463379, 0.52941179275512695, 0.52941179275512695), (0.5378151535987854, 0.5372549295425415, 0.5372549295425415), (0.54201680421829224, 0.54117649793624878, 0.54117649793624878), (0.54621851444244385, 0.54509806632995605, 0.54509806632995605), (0.55042016506195068, 0.54901963472366333, 0.54901963472366333), (0.55462187528610229, 0.55294120311737061, 0.55294120311737061), (0.55882352590560913, 0.55686277151107788, 0.55686277151107788), (0.56302523612976074, 0.56078433990478516, 0.56078433990478516), (0.56722688674926758, 0.56470590829849243, 0.56470590829849243), (0.57142859697341919, 0.56862747669219971, 0.56862747669219971), (0.57563024759292603, 0.57254904508590698, 0.57254904508590698), (0.57983195781707764, 0.57647061347961426, 0.57647061347961426), (0.58403360843658447, 0.58039218187332153, 0.58039218187332153), (0.58823531866073608, 0.58431375026702881, 0.58431375026702881), (0.59243696928024292, 0.58823531866073608, 0.58823531866073608), (0.59663867950439453, 0.59215688705444336, 0.59215688705444336), (0.60084033012390137, 0.60000002384185791, 0.60000002384185791), (0.60504204034805298, 0.60392159223556519, 0.60392159223556519), (0.60924369096755981, 0.60784316062927246, 0.60784316062927246), (0.61344540119171143, 0.61176472902297974, 0.61176472902297974), (0.61764705181121826, 0.61568629741668701, 0.61568629741668701), (0.62184876203536987, 0.61960786581039429, 0.61960786581039429), (0.62605041265487671, 0.62352943420410156, 0.62352943420410156), (0.63025212287902832, 0.62745100259780884, 0.62745100259780884), (0.63445377349853516, 0.63137257099151611, 0.63137257099151611), (0.63865548372268677, 0.63529413938522339, 0.63529413938522339), (0.6428571343421936, 0.63921570777893066, 0.63921570777893066), (0.64705884456634521, 0.64313727617263794, 0.64313727617263794), (0.65126049518585205, 0.64705884456634521, 0.64705884456634521), (0.65546220541000366, 0.65098041296005249, 0.65098041296005249), (0.6596638560295105, 0.65490198135375977, 0.65490198135375977), (0.66386556625366211, 0.66274511814117432, 0.66274511814117432), (0.66806721687316895, 0.66666668653488159, 0.66666668653488159), (0.67226892709732056, 0.67058825492858887, 0.67058825492858887), (0.67647057771682739, 0.67450982332229614, 0.67450982332229614), (0.680672287940979, 0.67843139171600342, 0.67843139171600342), (0.68487393856048584, 0.68235296010971069, 0.68235296010971069), (0.68907564878463745, 0.68627452850341797, 0.68627452850341797), (0.69327729940414429, 0.69019609689712524, 0.69019609689712524), (0.6974790096282959, 0.69411766529083252, 0.69411766529083252), (0.70168066024780273, 0.69803923368453979, 0.69803923368453979), (0.70588237047195435, 0.70196080207824707, 0.70196080207824707), (0.71008402109146118, 0.70588237047195435, 0.70588237047195435), (0.71428573131561279, 0.70980393886566162, 0.70980393886566162), (0.71848738193511963, 0.7137255072593689, 0.7137255072593689), (0.72268909215927124, 0.71764707565307617, 0.71764707565307617), (0.72689074277877808, 0.72549021244049072, 0.72549021244049072), (0.73109245300292969, 0.729411780834198, 0.729411780834198), (0.73529410362243652, 0.73333334922790527, 0.73333334922790527), (0.73949581384658813, 0.73725491762161255, 0.73725491762161255), (0.74369746446609497, 0.74117648601531982, 0.74117648601531982), (0.74789917469024658, 0.7450980544090271, 0.7450980544090271), (0.75210082530975342, 0.74901962280273438, 0.74901962280273438), (0.75630253553390503, 0.75294119119644165, 0.75294119119644165), (0.76050418615341187, 0.75686275959014893, 0.75686275959014893), (0.76470589637756348, 0.7607843279838562, 0.7607843279838562), (0.76890754699707031, 0.76470589637756348, 0.76470589637756348), (0.77310925722122192, 0.76862746477127075, 0.76862746477127075), (0.77731090784072876, 0.77254903316497803, 0.77254903316497803), (0.78151261806488037, 0.7764706015586853, 0.7764706015586853), (0.78571426868438721, 0.78039216995239258, 0.78039216995239258), (0.78991597890853882, 0.78823530673980713, 0.78823530673980713), (0.79411762952804565, 0.7921568751335144, 0.7921568751335144), (0.79831933975219727, 0.79607844352722168, 0.79607844352722168), (0.8025209903717041, 0.80000001192092896, 0.80000001192092896), (0.80672270059585571, 0.80392158031463623, 0.80392158031463623), (0.81092435121536255, 0.80784314870834351, 0.80784314870834351), (0.81512606143951416, 0.81176471710205078, 0.81176471710205078), (0.819327712059021, 0.81568628549575806, 0.81568628549575806), (0.82352942228317261, 0.81960785388946533, 0.81960785388946533), (0.82773107290267944, 0.82352942228317261, 0.82352942228317261), (0.83193278312683105, 0.82745099067687988, 0.82745099067687988), (0.83613443374633789, 0.83137255907058716, 0.83137255907058716), (0.8403361439704895, 0.83529412746429443, 0.83529412746429443), (0.84453779458999634, 0.83921569585800171, 0.83921569585800171), (0.84873950481414795, 0.84313726425170898, 0.84313726425170898), (0.85294115543365479, 0.85098040103912354, 0.85098040103912354), (0.8571428656578064, 0.85490196943283081, 0.85490196943283081), (0.86134451627731323, 0.85882353782653809, 0.85882353782653809), (0.86554622650146484, 0.86274510622024536, 0.86274510622024536), (0.86974787712097168, 0.86666667461395264, 0.86666667461395264), (0.87394958734512329, 0.87058824300765991, 0.87058824300765991), (0.87815123796463013, 0.87450981140136719, 0.87450981140136719), (0.88235294818878174, 0.87843137979507446, 0.87843137979507446), (0.88655459880828857, 0.88235294818878174, 0.88235294818878174), (0.89075630903244019, 0.88627451658248901, 0.88627451658248901), (0.89495795965194702, 0.89019608497619629, 0.89019608497619629), (0.89915966987609863, 0.89411765336990356, 0.89411765336990356), (0.90336132049560547, 0.89803922176361084, 0.89803922176361084), (0.90756303071975708, 0.90196079015731812, 0.90196079015731812), (0.91176468133926392, 0.90588235855102539, 0.90588235855102539), (0.91596639156341553, 0.91372549533843994, 0.91372549533843994), (0.92016804218292236, 0.91764706373214722, 0.91764706373214722), (0.92436975240707397, 0.92156863212585449, 0.92156863212585449), (0.92857140302658081, 0.92549020051956177, 0.92549020051956177), (0.93277311325073242, 0.92941176891326904, 0.92941176891326904), (0.93697476387023926, 0.93333333730697632, 0.93333333730697632), (0.94117647409439087, 0.93725490570068359, 0.93725490570068359), (0.94537812471389771, 0.94117647409439087, 0.94117647409439087), (0.94957983493804932, 0.94509804248809814, 0.94509804248809814), (0.95378148555755615, 0.94901961088180542, 0.94901961088180542), (0.95798319578170776, 0.9529411792755127, 0.9529411792755127), (0.9621848464012146, 0.95686274766921997, 0.95686274766921997), (0.96638655662536621, 0.96078431606292725, 0.96078431606292725), (0.97058820724487305, 0.96470588445663452, 0.96470588445663452), (0.97478991746902466, 0.9686274528503418, 0.9686274528503418), (0.97899156808853149, 0.97647058963775635, 0.97647058963775635), (0.98319327831268311, 0.98039215803146362, 0.98039215803146362), (0.98739492893218994, 0.9843137264251709, 0.9843137264251709), (0.99159663915634155, 0.98823529481887817, 0.98823529481887817), (0.99579828977584839, 0.99215686321258545, 0.99215686321258545), (1.0, 0.99607843160629272, 0.99607843160629272)], ) return ColorMapper.from_segment_map(_data, range=range, **traits) def gist_heat(range, **traits): """ Generator for the 'gist_heat' colormap from GIST. """ _data = dict( red = [(0.0, 0.0, 0.0), (0.0042016808874905109, 0.0039215688593685627, 0.0039215688593685627), (0.0084033617749810219, 0.0078431377187371254, 0.0078431377187371254), (0.012605042196810246, 0.015686275437474251, 0.015686275437474251), (0.016806723549962044, 0.019607843831181526, 0.019607843831181526), (0.021008403971791267, 0.027450980618596077, 0.027450980618596077), (0.025210084393620491, 0.031372550874948502, 0.031372550874948502), (0.029411764815449715, 0.039215687662363052, 0.039215687662363052), (0.033613447099924088, 0.043137256056070328, 0.043137256056070328), (0.037815127521753311, 0.050980392843484879, 0.050980392843484879), (0.042016807943582535, 0.058823529630899429, 0.058823529630899429), (0.046218488365411758, 0.066666670143604279, 0.066666670143604279), (0.050420168787240982, 0.070588238537311554, 0.070588238537311554), (0.054621849209070206, 0.078431375324726105, 0.078431375324726105), (0.058823529630899429, 0.08235294371843338, 0.08235294371843338), (0.063025213778018951, 0.090196080505847931, 0.090196080505847931), (0.067226894199848175, 0.094117648899555206, 0.094117648899555206), (0.071428574621677399, 0.10196078568696976, 0.10196078568696976), (0.075630255043506622, 0.10588235408067703, 0.10588235408067703), (0.079831935465335846, 0.10980392247438431, 0.10980392247438431), (0.08403361588716507, 0.11764705926179886, 0.11764705926179886), (0.088235296308994293, 0.12156862765550613, 0.12156862765550613), (0.092436976730823517, 0.12941177189350128, 0.12941177189350128), (0.09663865715265274, 0.13333334028720856, 0.13333334028720856), (0.10084033757448196, 0.14117647707462311, 0.14117647707462311), (0.10504201799631119, 0.14509804546833038, 0.14509804546833038), (0.10924369841814041, 0.15294118225574493, 0.15294118225574493), (0.11344537883996964, 0.15686275064945221, 0.15686275064945221), (0.11764705926179886, 0.16470588743686676, 0.16470588743686676), (0.12184873968362808, 0.16862745583057404, 0.16862745583057404), (0.1260504275560379, 0.18039216101169586, 0.18039216101169586), (0.13025210797786713, 0.18431372940540314, 0.18431372940540314), (0.13445378839969635, 0.19215686619281769, 0.19215686619281769), (0.13865546882152557, 0.19607843458652496, 0.19607843458652496), (0.1428571492433548, 0.20392157137393951, 0.20392157137393951), (0.14705882966518402, 0.20784313976764679, 0.20784313976764679), (0.15126051008701324, 0.21568627655506134, 0.21568627655506134), (0.15546219050884247, 0.21960784494876862, 0.21960784494876862), (0.15966387093067169, 0.22352941334247589, 0.22352941334247589), (0.16386555135250092, 0.23137255012989044, 0.23137255012989044), (0.16806723177433014, 0.23529411852359772, 0.23529411852359772), (0.17226891219615936, 0.24313725531101227, 0.24313725531101227), (0.17647059261798859, 0.24705882370471954, 0.24705882370471954), (0.18067227303981781, 0.25490197539329529, 0.25490197539329529), (0.18487395346164703, 0.25882354378700256, 0.25882354378700256), (0.18907563388347626, 0.26666668057441711, 0.26666668057441711), (0.19327731430530548, 0.27058824896812439, 0.27058824896812439), (0.1974789947271347, 0.27450981736183167, 0.27450981736183167), (0.20168067514896393, 0.28235295414924622, 0.28235295414924622), (0.20588235557079315, 0.28627452254295349, 0.28627452254295349), (0.21008403599262238, 0.29803922772407532, 0.29803922772407532), (0.2142857164144516, 0.30588236451148987, 0.30588236451148987), (0.21848739683628082, 0.30980393290519714, 0.30980393290519714), (0.22268907725811005, 0.31764706969261169, 0.31764706969261169), (0.22689075767993927, 0.32156863808631897, 0.32156863808631897), (0.23109243810176849, 0.32941177487373352, 0.32941177487373352), (0.23529411852359772, 0.3333333432674408, 0.3333333432674408), (0.23949579894542694, 0.33725491166114807, 0.33725491166114807), (0.24369747936725616, 0.34509804844856262, 0.34509804844856262), (0.24789915978908539, 0.3490196168422699, 0.3490196168422699), (0.25210085511207581, 0.36078432202339172, 0.36078432202339172), (0.25630253553390503, 0.36862745881080627, 0.36862745881080627), (0.26050421595573425, 0.37254902720451355, 0.37254902720451355), (0.26470589637756348, 0.3803921639919281, 0.3803921639919281), (0.2689075767993927, 0.38431373238563538, 0.38431373238563538), (0.27310925722122192, 0.38823530077934265, 0.38823530077934265), (0.27731093764305115, 0.3960784375667572, 0.3960784375667572), (0.28151261806488037, 0.40000000596046448, 0.40000000596046448), (0.28571429848670959, 0.40784314274787903, 0.40784314274787903), (0.28991597890853882, 0.4117647111415863, 0.4117647111415863), (0.29411765933036804, 0.42352941632270813, 0.42352941632270813), (0.29831933975219727, 0.43137255311012268, 0.43137255311012268), (0.30252102017402649, 0.43529412150382996, 0.43529412150382996), (0.30672270059585571, 0.44313725829124451, 0.44313725829124451), (0.31092438101768494, 0.44705882668495178, 0.44705882668495178), (0.31512606143951416, 0.45098039507865906, 0.45098039507865906), (0.31932774186134338, 0.45882353186607361, 0.45882353186607361), (0.32352942228317261, 0.46274510025978088, 0.46274510025978088), (0.32773110270500183, 0.47058823704719543, 0.47058823704719543), (0.33193278312683105, 0.47450980544090271, 0.47450980544090271), (0.33613446354866028, 0.48235294222831726, 0.48235294222831726), (0.3403361439704895, 0.48627451062202454, 0.48627451062202454), (0.34453782439231873, 0.49411764740943909, 0.49411764740943909), (0.34873950481414795, 0.49803921580314636, 0.49803921580314636), (0.35294118523597717, 0.50196081399917603, 0.50196081399917603), (0.3571428656578064, 0.50980395078659058, 0.50980395078659058), (0.36134454607963562, 0.51372551918029785, 0.51372551918029785), (0.36554622650146484, 0.5215686559677124, 0.5215686559677124), (0.36974790692329407, 0.52549022436141968, 0.52549022436141968), (0.37394958734512329, 0.53333336114883423, 0.53333336114883423), (0.37815126776695251, 0.54509806632995605, 0.54509806632995605), (0.38235294818878174, 0.54901963472366333, 0.54901963472366333), (0.38655462861061096, 0.55294120311737061, 0.55294120311737061), (0.39075630903244019, 0.56078433990478516, 0.56078433990478516), (0.39495798945426941, 0.56470590829849243, 0.56470590829849243), (0.39915966987609863, 0.57254904508590698, 0.57254904508590698), (0.40336135029792786, 0.57647061347961426, 0.57647061347961426), (0.40756303071975708, 0.58431375026702881, 0.58431375026702881), (0.4117647111415863, 0.58823531866073608, 0.58823531866073608), (0.41596639156341553, 0.59607845544815063, 0.59607845544815063), (0.42016807198524475, 0.60000002384185791, 0.60000002384185791), (0.42436975240707397, 0.60784316062927246, 0.60784316062927246), (0.4285714328289032, 0.61176472902297974, 0.61176472902297974), (0.43277311325073242, 0.61568629741668701, 0.61568629741668701), (0.43697479367256165, 0.62352943420410156, 0.62352943420410156), (0.44117647409439087, 0.62745100259780884, 0.62745100259780884), (0.44537815451622009, 0.63529413938522339, 0.63529413938522339), (0.44957983493804932, 0.63921570777893066, 0.63921570777893066), (0.45378151535987854, 0.64705884456634521, 0.64705884456634521), (0.45798319578170776, 0.65098041296005249, 0.65098041296005249), (0.46218487620353699, 0.66274511814117432, 0.66274511814117432), (0.46638655662536621, 0.66666668653488159, 0.66666668653488159), (0.47058823704719543, 0.67450982332229614, 0.67450982332229614), (0.47478991746902466, 0.67843139171600342, 0.67843139171600342), (0.47899159789085388, 0.68627452850341797, 0.68627452850341797), (0.48319327831268311, 0.69019609689712524, 0.69019609689712524), (0.48739495873451233, 0.69803923368453979, 0.69803923368453979), (0.49159663915634155, 0.70196080207824707, 0.70196080207824707), (0.49579831957817078, 0.70980393886566162, 0.70980393886566162), (0.5, 0.7137255072593689, 0.7137255072593689), (0.50420171022415161, 0.72549021244049072, 0.72549021244049072), (0.50840336084365845, 0.729411780834198, 0.729411780834198), (0.51260507106781006, 0.73725491762161255, 0.73725491762161255), (0.51680672168731689, 0.74117648601531982, 0.74117648601531982), (0.52100843191146851, 0.74901962280273438, 0.74901962280273438), (0.52521008253097534, 0.75294119119644165, 0.75294119119644165), (0.52941179275512695, 0.7607843279838562, 0.7607843279838562), (0.53361344337463379, 0.76470589637756348, 0.76470589637756348), (0.5378151535987854, 0.77254903316497803, 0.77254903316497803), (0.54201680421829224, 0.7764706015586853, 0.7764706015586853), (0.54621851444244385, 0.78823530673980713, 0.78823530673980713), (0.55042016506195068, 0.7921568751335144, 0.7921568751335144), (0.55462187528610229, 0.80000001192092896, 0.80000001192092896), (0.55882352590560913, 0.80392158031463623, 0.80392158031463623), (0.56302523612976074, 0.81176471710205078, 0.81176471710205078), (0.56722688674926758, 0.81568628549575806, 0.81568628549575806), (0.57142859697341919, 0.82352942228317261, 0.82352942228317261), (0.57563024759292603, 0.82745099067687988, 0.82745099067687988), (0.57983195781707764, 0.83137255907058716, 0.83137255907058716), (0.58403360843658447, 0.83921569585800171, 0.83921569585800171), (0.58823531866073608, 0.84313726425170898, 0.84313726425170898), (0.59243696928024292, 0.85098040103912354, 0.85098040103912354), (0.59663867950439453, 0.85490196943283081, 0.85490196943283081), (0.60084033012390137, 0.86274510622024536, 0.86274510622024536), (0.60504204034805298, 0.86666667461395264, 0.86666667461395264), (0.60924369096755981, 0.87450981140136719, 0.87450981140136719), (0.61344540119171143, 0.87843137979507446, 0.87843137979507446), (0.61764705181121826, 0.88627451658248901, 0.88627451658248901), (0.62184876203536987, 0.89019608497619629, 0.89019608497619629), (0.62605041265487671, 0.89411765336990356, 0.89411765336990356), (0.63025212287902832, 0.90588235855102539, 0.90588235855102539), (0.63445377349853516, 0.91372549533843994, 0.91372549533843994), (0.63865548372268677, 0.91764706373214722, 0.91764706373214722), (0.6428571343421936, 0.92549020051956177, 0.92549020051956177), (0.64705884456634521, 0.92941176891326904, 0.92941176891326904), (0.65126049518585205, 0.93725490570068359, 0.93725490570068359), (0.65546220541000366, 0.94117647409439087, 0.94117647409439087), (0.6596638560295105, 0.94509804248809814, 0.94509804248809814), (0.66386556625366211, 0.9529411792755127, 0.9529411792755127), (0.66806721687316895, 0.95686274766921997, 0.95686274766921997), (0.67226892709732056, 0.96470588445663452, 0.96470588445663452), (0.67647057771682739, 0.9686274528503418, 0.9686274528503418), (0.680672287940979, 0.97647058963775635, 0.97647058963775635), (0.68487393856048584, 0.98039215803146362, 0.98039215803146362), (0.68907564878463745, 0.98823529481887817, 0.98823529481887817), (0.69327729940414429, 0.99215686321258545, 0.99215686321258545), (0.6974790096282959, 1.0, 1.0), (0.70168066024780273, 1.0, 1.0), (0.70588237047195435, 1.0, 1.0), (0.71008402109146118, 1.0, 1.0), (0.71428573131561279, 1.0, 1.0), (0.71848738193511963, 1.0, 1.0), (0.72268909215927124, 1.0, 1.0), (0.72689074277877808, 1.0, 1.0), (0.73109245300292969, 1.0, 1.0), (0.73529410362243652, 1.0, 1.0), (0.73949581384658813, 1.0, 1.0), (0.74369746446609497, 1.0, 1.0), (0.74789917469024658, 1.0, 1.0), (0.75210082530975342, 1.0, 1.0), (0.75630253553390503, 1.0, 1.0), (0.76050418615341187, 1.0, 1.0), (0.76470589637756348, 1.0, 1.0), (0.76890754699707031, 1.0, 1.0), (0.77310925722122192, 1.0, 1.0), (0.77731090784072876, 1.0, 1.0), (0.78151261806488037, 1.0, 1.0), (0.78571426868438721, 1.0, 1.0), (0.78991597890853882, 1.0, 1.0), (0.79411762952804565, 1.0, 1.0), (0.79831933975219727, 1.0, 1.0), (0.8025209903717041, 1.0, 1.0), (0.80672270059585571, 1.0, 1.0), (0.81092435121536255, 1.0, 1.0), (0.81512606143951416, 1.0, 1.0), (0.819327712059021, 1.0, 1.0), (0.82352942228317261, 1.0, 1.0), (0.82773107290267944, 1.0, 1.0), (0.83193278312683105, 1.0, 1.0), (0.83613443374633789, 1.0, 1.0), (0.8403361439704895, 1.0, 1.0), (0.84453779458999634, 1.0, 1.0), (0.84873950481414795, 1.0, 1.0), (0.85294115543365479, 1.0, 1.0), (0.8571428656578064, 1.0, 1.0), (0.86134451627731323, 1.0, 1.0), (0.86554622650146484, 1.0, 1.0), (0.86974787712097168, 1.0, 1.0), (0.87394958734512329, 1.0, 1.0), (0.87815123796463013, 1.0, 1.0), (0.88235294818878174, 1.0, 1.0), (0.88655459880828857, 1.0, 1.0), (0.89075630903244019, 1.0, 1.0), (0.89495795965194702, 1.0, 1.0), (0.89915966987609863, 1.0, 1.0), (0.90336132049560547, 1.0, 1.0), (0.90756303071975708, 1.0, 1.0), (0.91176468133926392, 1.0, 1.0), (0.91596639156341553, 1.0, 1.0), (0.92016804218292236, 1.0, 1.0), (0.92436975240707397, 1.0, 1.0), (0.92857140302658081, 1.0, 1.0), (0.93277311325073242, 1.0, 1.0), (0.93697476387023926, 1.0, 1.0), (0.94117647409439087, 1.0, 1.0), (0.94537812471389771, 1.0, 1.0), (0.94957983493804932, 1.0, 1.0), (0.95378148555755615, 1.0, 1.0), (0.95798319578170776, 1.0, 1.0), (0.9621848464012146, 1.0, 1.0), (0.96638655662536621, 1.0, 1.0), (0.97058820724487305, 1.0, 1.0), (0.97478991746902466, 1.0, 1.0), (0.97899156808853149, 1.0, 1.0), (0.98319327831268311, 1.0, 1.0), (0.98739492893218994, 1.0, 1.0), (0.99159663915634155, 1.0, 1.0), (0.99579828977584839, 1.0, 1.0), (1.0, 1.0, 1.0)], green = [(0.0, 0.0, 0.0), (0.0042016808874905109, 0.0, 0.0), (0.0084033617749810219, 0.0, 0.0), (0.012605042196810246, 0.0, 0.0), (0.016806723549962044, 0.0, 0.0), (0.021008403971791267, 0.0, 0.0), (0.025210084393620491, 0.0, 0.0), (0.029411764815449715, 0.0, 0.0), (0.033613447099924088, 0.0, 0.0), (0.037815127521753311, 0.0, 0.0), (0.042016807943582535, 0.0, 0.0), (0.046218488365411758, 0.0, 0.0), (0.050420168787240982, 0.0, 0.0), (0.054621849209070206, 0.0, 0.0), (0.058823529630899429, 0.0, 0.0), (0.063025213778018951, 0.0, 0.0), (0.067226894199848175, 0.0, 0.0), (0.071428574621677399, 0.0, 0.0), (0.075630255043506622, 0.0, 0.0), (0.079831935465335846, 0.0, 0.0), (0.08403361588716507, 0.0, 0.0), (0.088235296308994293, 0.0, 0.0), (0.092436976730823517, 0.0, 0.0), (0.09663865715265274, 0.0, 0.0), (0.10084033757448196, 0.0, 0.0), (0.10504201799631119, 0.0, 0.0), (0.10924369841814041, 0.0, 0.0), (0.11344537883996964, 0.0, 0.0), (0.11764705926179886, 0.0, 0.0), (0.12184873968362808, 0.0, 0.0), (0.1260504275560379, 0.0, 0.0), (0.13025210797786713, 0.0, 0.0), (0.13445378839969635, 0.0, 0.0), (0.13865546882152557, 0.0, 0.0), (0.1428571492433548, 0.0, 0.0), (0.14705882966518402, 0.0, 0.0), (0.15126051008701324, 0.0, 0.0), (0.15546219050884247, 0.0, 0.0), (0.15966387093067169, 0.0, 0.0), (0.16386555135250092, 0.0, 0.0), (0.16806723177433014, 0.0, 0.0), (0.17226891219615936, 0.0, 0.0), (0.17647059261798859, 0.0, 0.0), (0.18067227303981781, 0.0, 0.0), (0.18487395346164703, 0.0, 0.0), (0.18907563388347626, 0.0, 0.0), (0.19327731430530548, 0.0, 0.0), (0.1974789947271347, 0.0, 0.0), (0.20168067514896393, 0.0, 0.0), (0.20588235557079315, 0.0, 0.0), (0.21008403599262238, 0.0, 0.0), (0.2142857164144516, 0.0, 0.0), (0.21848739683628082, 0.0, 0.0), (0.22268907725811005, 0.0, 0.0), (0.22689075767993927, 0.0, 0.0), (0.23109243810176849, 0.0, 0.0), (0.23529411852359772, 0.0, 0.0), (0.23949579894542694, 0.0, 0.0), (0.24369747936725616, 0.0, 0.0), (0.24789915978908539, 0.0, 0.0), (0.25210085511207581, 0.0, 0.0), (0.25630253553390503, 0.0, 0.0), (0.26050421595573425, 0.0, 0.0), (0.26470589637756348, 0.0, 0.0), (0.2689075767993927, 0.0, 0.0), (0.27310925722122192, 0.0, 0.0), (0.27731093764305115, 0.0, 0.0), (0.28151261806488037, 0.0, 0.0), (0.28571429848670959, 0.0, 0.0), (0.28991597890853882, 0.0, 0.0), (0.29411765933036804, 0.0, 0.0), (0.29831933975219727, 0.0, 0.0), (0.30252102017402649, 0.0, 0.0), (0.30672270059585571, 0.0, 0.0), (0.31092438101768494, 0.0, 0.0), (0.31512606143951416, 0.0, 0.0), (0.31932774186134338, 0.0, 0.0), (0.32352942228317261, 0.0, 0.0), (0.32773110270500183, 0.0, 0.0), (0.33193278312683105, 0.0, 0.0), (0.33613446354866028, 0.0, 0.0), (0.3403361439704895, 0.0, 0.0), (0.34453782439231873, 0.0, 0.0), (0.34873950481414795, 0.0, 0.0), (0.35294118523597717, 0.0, 0.0), (0.3571428656578064, 0.0, 0.0), (0.36134454607963562, 0.0, 0.0), (0.36554622650146484, 0.0, 0.0), (0.36974790692329407, 0.0, 0.0), (0.37394958734512329, 0.0, 0.0), (0.37815126776695251, 0.0, 0.0), (0.38235294818878174, 0.0, 0.0), (0.38655462861061096, 0.0, 0.0), (0.39075630903244019, 0.0, 0.0), (0.39495798945426941, 0.0, 0.0), (0.39915966987609863, 0.0, 0.0), (0.40336135029792786, 0.0, 0.0), (0.40756303071975708, 0.0, 0.0), (0.4117647111415863, 0.0, 0.0), (0.41596639156341553, 0.0, 0.0), (0.42016807198524475, 0.0, 0.0), (0.42436975240707397, 0.0, 0.0), (0.4285714328289032, 0.0, 0.0), (0.43277311325073242, 0.0, 0.0), (0.43697479367256165, 0.0, 0.0), (0.44117647409439087, 0.0, 0.0), (0.44537815451622009, 0.0, 0.0), (0.44957983493804932, 0.0, 0.0), (0.45378151535987854, 0.0, 0.0), (0.45798319578170776, 0.0, 0.0), (0.46218487620353699, 0.0, 0.0), (0.46638655662536621, 0.0, 0.0), (0.47058823704719543, 0.0, 0.0), (0.47478991746902466, 0.0, 0.0), (0.47899159789085388, 0.0039215688593685627, 0.0039215688593685627), (0.48319327831268311, 0.011764706112444401, 0.011764706112444401), (0.48739495873451233, 0.019607843831181526, 0.019607843831181526), (0.49159663915634155, 0.027450980618596077, 0.027450980618596077), (0.49579831957817078, 0.035294119268655777, 0.035294119268655777), (0.5, 0.043137256056070328, 0.043137256056070328), (0.50420171022415161, 0.058823529630899429, 0.058823529630899429), (0.50840336084365845, 0.066666670143604279, 0.066666670143604279), (0.51260507106781006, 0.070588238537311554, 0.070588238537311554), (0.51680672168731689, 0.078431375324726105, 0.078431375324726105), (0.52100843191146851, 0.086274512112140656, 0.086274512112140656), (0.52521008253097534, 0.094117648899555206, 0.094117648899555206), (0.52941179275512695, 0.10196078568696976, 0.10196078568696976), (0.53361344337463379, 0.10980392247438431, 0.10980392247438431), (0.5378151535987854, 0.11764705926179886, 0.11764705926179886), (0.54201680421829224, 0.12549020349979401, 0.12549020349979401), (0.54621851444244385, 0.13725490868091583, 0.13725490868091583), (0.55042016506195068, 0.14509804546833038, 0.14509804546833038), (0.55462187528610229, 0.15294118225574493, 0.15294118225574493), (0.55882352590560913, 0.16078431904315948, 0.16078431904315948), (0.56302523612976074, 0.16862745583057404, 0.16862745583057404), (0.56722688674926758, 0.17647059261798859, 0.17647059261798859), (0.57142859697341919, 0.18431372940540314, 0.18431372940540314), (0.57563024759292603, 0.19215686619281769, 0.19215686619281769), (0.57983195781707764, 0.20000000298023224, 0.20000000298023224), (0.58403360843658447, 0.20392157137393951, 0.20392157137393951), (0.58823531866073608, 0.21176470816135406, 0.21176470816135406), (0.59243696928024292, 0.21960784494876862, 0.21960784494876862), (0.59663867950439453, 0.22745098173618317, 0.22745098173618317), (0.60084033012390137, 0.23529411852359772, 0.23529411852359772), (0.60504204034805298, 0.24313725531101227, 0.24313725531101227), (0.60924369096755981, 0.25098040699958801, 0.25098040699958801), (0.61344540119171143, 0.25882354378700256, 0.25882354378700256), (0.61764705181121826, 0.26666668057441711, 0.26666668057441711), (0.62184876203536987, 0.27058824896812439, 0.27058824896812439), (0.62605041265487671, 0.27843138575553894, 0.27843138575553894), (0.63025212287902832, 0.29411765933036804, 0.29411765933036804), (0.63445377349853516, 0.30196079611778259, 0.30196079611778259), (0.63865548372268677, 0.30980393290519714, 0.30980393290519714), (0.6428571343421936, 0.31764706969261169, 0.31764706969261169), (0.64705884456634521, 0.32549020648002625, 0.32549020648002625), (0.65126049518585205, 0.3333333432674408, 0.3333333432674408), (0.65546220541000366, 0.33725491166114807, 0.33725491166114807), (0.6596638560295105, 0.34509804844856262, 0.34509804844856262), (0.66386556625366211, 0.35294118523597717, 0.35294118523597717), (0.66806721687316895, 0.36078432202339172, 0.36078432202339172), (0.67226892709732056, 0.36862745881080627, 0.36862745881080627), (0.67647057771682739, 0.37647059559822083, 0.37647059559822083), (0.680672287940979, 0.38431373238563538, 0.38431373238563538), (0.68487393856048584, 0.39215686917304993, 0.39215686917304993), (0.68907564878463745, 0.40000000596046448, 0.40000000596046448), (0.69327729940414429, 0.40392157435417175, 0.40392157435417175), (0.6974790096282959, 0.4117647111415863, 0.4117647111415863), (0.70168066024780273, 0.41960784792900085, 0.41960784792900085), (0.70588237047195435, 0.42745098471641541, 0.42745098471641541), (0.71008402109146118, 0.43529412150382996, 0.43529412150382996), (0.71428573131561279, 0.45098039507865906, 0.45098039507865906), (0.71848738193511963, 0.45882353186607361, 0.45882353186607361), (0.72268909215927124, 0.46666666865348816, 0.46666666865348816), (0.72689074277877808, 0.47058823704719543, 0.47058823704719543), (0.73109245300292969, 0.47843137383460999, 0.47843137383460999), (0.73529410362243652, 0.48627451062202454, 0.48627451062202454), (0.73949581384658813, 0.49411764740943909, 0.49411764740943909), (0.74369746446609497, 0.50196081399917603, 0.50196081399917603), (0.74789917469024658, 0.50980395078659058, 0.50980395078659058), (0.75210082530975342, 0.51764708757400513, 0.51764708757400513), (0.75630253553390503, 0.53333336114883423, 0.53333336114883423), (0.76050418615341187, 0.5372549295425415, 0.5372549295425415), (0.76470589637756348, 0.54509806632995605, 0.54509806632995605), (0.76890754699707031, 0.55294120311737061, 0.55294120311737061), (0.77310925722122192, 0.56078433990478516, 0.56078433990478516), (0.77731090784072876, 0.56862747669219971, 0.56862747669219971), (0.78151261806488037, 0.57647061347961426, 0.57647061347961426), (0.78571426868438721, 0.58431375026702881, 0.58431375026702881), (0.78991597890853882, 0.59215688705444336, 0.59215688705444336), (0.79411762952804565, 0.60000002384185791, 0.60000002384185791), (0.79831933975219727, 0.61176472902297974, 0.61176472902297974), (0.8025209903717041, 0.61960786581039429, 0.61960786581039429), (0.80672270059585571, 0.62745100259780884, 0.62745100259780884), (0.81092435121536255, 0.63529413938522339, 0.63529413938522339), (0.81512606143951416, 0.64313727617263794, 0.64313727617263794), (0.819327712059021, 0.65098041296005249, 0.65098041296005249), (0.82352942228317261, 0.65882354974746704, 0.65882354974746704), (0.82773107290267944, 0.66666668653488159, 0.66666668653488159), (0.83193278312683105, 0.67058825492858887, 0.67058825492858887), (0.83613443374633789, 0.67843139171600342, 0.67843139171600342), (0.8403361439704895, 0.68627452850341797, 0.68627452850341797), (0.84453779458999634, 0.69411766529083252, 0.69411766529083252), (0.84873950481414795, 0.70196080207824707, 0.70196080207824707), (0.85294115543365479, 0.70980393886566162, 0.70980393886566162), (0.8571428656578064, 0.71764707565307617, 0.71764707565307617), (0.86134451627731323, 0.72549021244049072, 0.72549021244049072), (0.86554622650146484, 0.73333334922790527, 0.73333334922790527), (0.86974787712097168, 0.73725491762161255, 0.73725491762161255), (0.87394958734512329, 0.7450980544090271, 0.7450980544090271), (0.87815123796463013, 0.75294119119644165, 0.75294119119644165), (0.88235294818878174, 0.76862746477127075, 0.76862746477127075), (0.88655459880828857, 0.7764706015586853, 0.7764706015586853), (0.89075630903244019, 0.78431373834609985, 0.78431373834609985), (0.89495795965194702, 0.7921568751335144, 0.7921568751335144), (0.89915966987609863, 0.80000001192092896, 0.80000001192092896), (0.90336132049560547, 0.80392158031463623, 0.80392158031463623), (0.90756303071975708, 0.81176471710205078, 0.81176471710205078), (0.91176468133926392, 0.81960785388946533, 0.81960785388946533), (0.91596639156341553, 0.82745099067687988, 0.82745099067687988), (0.92016804218292236, 0.83529412746429443, 0.83529412746429443), (0.92436975240707397, 0.84313726425170898, 0.84313726425170898), (0.92857140302658081, 0.85098040103912354, 0.85098040103912354), (0.93277311325073242, 0.85882353782653809, 0.85882353782653809), (0.93697476387023926, 0.86666667461395264, 0.86666667461395264), (0.94117647409439087, 0.87058824300765991, 0.87058824300765991), (0.94537812471389771, 0.87843137979507446, 0.87843137979507446), (0.94957983493804932, 0.88627451658248901, 0.88627451658248901), (0.95378148555755615, 0.89411765336990356, 0.89411765336990356), (0.95798319578170776, 0.90196079015731812, 0.90196079015731812), (0.9621848464012146, 0.90980392694473267, 0.90980392694473267), (0.96638655662536621, 0.92549020051956177, 0.92549020051956177), (0.97058820724487305, 0.93333333730697632, 0.93333333730697632), (0.97478991746902466, 0.93725490570068359, 0.93725490570068359), (0.97899156808853149, 0.94509804248809814, 0.94509804248809814), (0.98319327831268311, 0.9529411792755127, 0.9529411792755127), (0.98739492893218994, 0.96078431606292725, 0.96078431606292725), (0.99159663915634155, 0.9686274528503418, 0.9686274528503418), (0.99579828977584839, 0.97647058963775635, 0.97647058963775635), (1.0, 0.9843137264251709, 0.9843137264251709)], blue = [(0.0, 0.0, 0.0), (0.0042016808874905109, 0.0, 0.0), (0.0084033617749810219, 0.0, 0.0), (0.012605042196810246, 0.0, 0.0), (0.016806723549962044, 0.0, 0.0), (0.021008403971791267, 0.0, 0.0), (0.025210084393620491, 0.0, 0.0), (0.029411764815449715, 0.0, 0.0), (0.033613447099924088, 0.0, 0.0), (0.037815127521753311, 0.0, 0.0), (0.042016807943582535, 0.0, 0.0), (0.046218488365411758, 0.0, 0.0), (0.050420168787240982, 0.0, 0.0), (0.054621849209070206, 0.0, 0.0), (0.058823529630899429, 0.0, 0.0), (0.063025213778018951, 0.0, 0.0), (0.067226894199848175, 0.0, 0.0), (0.071428574621677399, 0.0, 0.0), (0.075630255043506622, 0.0, 0.0), (0.079831935465335846, 0.0, 0.0), (0.08403361588716507, 0.0, 0.0), (0.088235296308994293, 0.0, 0.0), (0.092436976730823517, 0.0, 0.0), (0.09663865715265274, 0.0, 0.0), (0.10084033757448196, 0.0, 0.0), (0.10504201799631119, 0.0, 0.0), (0.10924369841814041, 0.0, 0.0), (0.11344537883996964, 0.0, 0.0), (0.11764705926179886, 0.0, 0.0), (0.12184873968362808, 0.0, 0.0), (0.1260504275560379, 0.0, 0.0), (0.13025210797786713, 0.0, 0.0), (0.13445378839969635, 0.0, 0.0), (0.13865546882152557, 0.0, 0.0), (0.1428571492433548, 0.0, 0.0), (0.14705882966518402, 0.0, 0.0), (0.15126051008701324, 0.0, 0.0), (0.15546219050884247, 0.0, 0.0), (0.15966387093067169, 0.0, 0.0), (0.16386555135250092, 0.0, 0.0), (0.16806723177433014, 0.0, 0.0), (0.17226891219615936, 0.0, 0.0), (0.17647059261798859, 0.0, 0.0), (0.18067227303981781, 0.0, 0.0), (0.18487395346164703, 0.0, 0.0), (0.18907563388347626, 0.0, 0.0), (0.19327731430530548, 0.0, 0.0), (0.1974789947271347, 0.0, 0.0), (0.20168067514896393, 0.0, 0.0), (0.20588235557079315, 0.0, 0.0), (0.21008403599262238, 0.0, 0.0), (0.2142857164144516, 0.0, 0.0), (0.21848739683628082, 0.0, 0.0), (0.22268907725811005, 0.0, 0.0), (0.22689075767993927, 0.0, 0.0), (0.23109243810176849, 0.0, 0.0), (0.23529411852359772, 0.0, 0.0), (0.23949579894542694, 0.0, 0.0), (0.24369747936725616, 0.0, 0.0), (0.24789915978908539, 0.0, 0.0), (0.25210085511207581, 0.0, 0.0), (0.25630253553390503, 0.0, 0.0), (0.26050421595573425, 0.0, 0.0), (0.26470589637756348, 0.0, 0.0), (0.2689075767993927, 0.0, 0.0), (0.27310925722122192, 0.0, 0.0), (0.27731093764305115, 0.0, 0.0), (0.28151261806488037, 0.0, 0.0), (0.28571429848670959, 0.0, 0.0), (0.28991597890853882, 0.0, 0.0), (0.29411765933036804, 0.0, 0.0), (0.29831933975219727, 0.0, 0.0), (0.30252102017402649, 0.0, 0.0), (0.30672270059585571, 0.0, 0.0), (0.31092438101768494, 0.0, 0.0), (0.31512606143951416, 0.0, 0.0), (0.31932774186134338, 0.0, 0.0), (0.32352942228317261, 0.0, 0.0), (0.32773110270500183, 0.0, 0.0), (0.33193278312683105, 0.0, 0.0), (0.33613446354866028, 0.0, 0.0), (0.3403361439704895, 0.0, 0.0), (0.34453782439231873, 0.0, 0.0), (0.34873950481414795, 0.0, 0.0), (0.35294118523597717, 0.0, 0.0), (0.3571428656578064, 0.0, 0.0), (0.36134454607963562, 0.0, 0.0), (0.36554622650146484, 0.0, 0.0), (0.36974790692329407, 0.0, 0.0), (0.37394958734512329, 0.0, 0.0), (0.37815126776695251, 0.0, 0.0), (0.38235294818878174, 0.0, 0.0), (0.38655462861061096, 0.0, 0.0), (0.39075630903244019, 0.0, 0.0), (0.39495798945426941, 0.0, 0.0), (0.39915966987609863, 0.0, 0.0), (0.40336135029792786, 0.0, 0.0), (0.40756303071975708, 0.0, 0.0), (0.4117647111415863, 0.0, 0.0), (0.41596639156341553, 0.0, 0.0), (0.42016807198524475, 0.0, 0.0), (0.42436975240707397, 0.0, 0.0), (0.4285714328289032, 0.0, 0.0), (0.43277311325073242, 0.0, 0.0), (0.43697479367256165, 0.0, 0.0), (0.44117647409439087, 0.0, 0.0), (0.44537815451622009, 0.0, 0.0), (0.44957983493804932, 0.0, 0.0), (0.45378151535987854, 0.0, 0.0), (0.45798319578170776, 0.0, 0.0), (0.46218487620353699, 0.0, 0.0), (0.46638655662536621, 0.0, 0.0), (0.47058823704719543, 0.0, 0.0), (0.47478991746902466, 0.0, 0.0), (0.47899159789085388, 0.0, 0.0), (0.48319327831268311, 0.0, 0.0), (0.48739495873451233, 0.0, 0.0), (0.49159663915634155, 0.0, 0.0), (0.49579831957817078, 0.0, 0.0), (0.5, 0.0, 0.0), (0.50420171022415161, 0.0, 0.0), (0.50840336084365845, 0.0, 0.0), (0.51260507106781006, 0.0, 0.0), (0.51680672168731689, 0.0, 0.0), (0.52100843191146851, 0.0, 0.0), (0.52521008253097534, 0.0, 0.0), (0.52941179275512695, 0.0, 0.0), (0.53361344337463379, 0.0, 0.0), (0.5378151535987854, 0.0, 0.0), (0.54201680421829224, 0.0, 0.0), (0.54621851444244385, 0.0, 0.0), (0.55042016506195068, 0.0, 0.0), (0.55462187528610229, 0.0, 0.0), (0.55882352590560913, 0.0, 0.0), (0.56302523612976074, 0.0, 0.0), (0.56722688674926758, 0.0, 0.0), (0.57142859697341919, 0.0, 0.0), (0.57563024759292603, 0.0, 0.0), (0.57983195781707764, 0.0, 0.0), (0.58403360843658447, 0.0, 0.0), (0.58823531866073608, 0.0, 0.0), (0.59243696928024292, 0.0, 0.0), (0.59663867950439453, 0.0, 0.0), (0.60084033012390137, 0.0, 0.0), (0.60504204034805298, 0.0, 0.0), (0.60924369096755981, 0.0, 0.0), (0.61344540119171143, 0.0, 0.0), (0.61764705181121826, 0.0, 0.0), (0.62184876203536987, 0.0, 0.0), (0.62605041265487671, 0.0, 0.0), (0.63025212287902832, 0.0, 0.0), (0.63445377349853516, 0.0, 0.0), (0.63865548372268677, 0.0, 0.0), (0.6428571343421936, 0.0, 0.0), (0.64705884456634521, 0.0, 0.0), (0.65126049518585205, 0.0, 0.0), (0.65546220541000366, 0.0, 0.0), (0.6596638560295105, 0.0, 0.0), (0.66386556625366211, 0.0, 0.0), (0.66806721687316895, 0.0, 0.0), (0.67226892709732056, 0.0, 0.0), (0.67647057771682739, 0.0, 0.0), (0.680672287940979, 0.0, 0.0), (0.68487393856048584, 0.0, 0.0), (0.68907564878463745, 0.0, 0.0), (0.69327729940414429, 0.0, 0.0), (0.6974790096282959, 0.0, 0.0), (0.70168066024780273, 0.0, 0.0), (0.70588237047195435, 0.0, 0.0), (0.71008402109146118, 0.0, 0.0), (0.71428573131561279, 0.0, 0.0), (0.71848738193511963, 0.0, 0.0), (0.72268909215927124, 0.0, 0.0), (0.72689074277877808, 0.0, 0.0), (0.73109245300292969, 0.0, 0.0), (0.73529410362243652, 0.0, 0.0), (0.73949581384658813, 0.0, 0.0), (0.74369746446609497, 0.0, 0.0), (0.74789917469024658, 0.0, 0.0), (0.75210082530975342, 0.0, 0.0), (0.75630253553390503, 0.027450980618596077, 0.027450980618596077), (0.76050418615341187, 0.043137256056070328, 0.043137256056070328), (0.76470589637756348, 0.058823529630899429, 0.058823529630899429), (0.76890754699707031, 0.074509806931018829, 0.074509806931018829), (0.77310925722122192, 0.090196080505847931, 0.090196080505847931), (0.77731090784072876, 0.10588235408067703, 0.10588235408067703), (0.78151261806488037, 0.12156862765550613, 0.12156862765550613), (0.78571426868438721, 0.13725490868091583, 0.13725490868091583), (0.78991597890853882, 0.15294118225574493, 0.15294118225574493), (0.79411762952804565, 0.16862745583057404, 0.16862745583057404), (0.79831933975219727, 0.20000000298023224, 0.20000000298023224), (0.8025209903717041, 0.21176470816135406, 0.21176470816135406), (0.80672270059585571, 0.22745098173618317, 0.22745098173618317), (0.81092435121536255, 0.24313725531101227, 0.24313725531101227), (0.81512606143951416, 0.25882354378700256, 0.25882354378700256), (0.819327712059021, 0.27450981736183167, 0.27450981736183167), (0.82352942228317261, 0.29019609093666077, 0.29019609093666077), (0.82773107290267944, 0.30588236451148987, 0.30588236451148987), (0.83193278312683105, 0.32156863808631897, 0.32156863808631897), (0.83613443374633789, 0.33725491166114807, 0.33725491166114807), (0.8403361439704895, 0.35294118523597717, 0.35294118523597717), (0.84453779458999634, 0.36862745881080627, 0.36862745881080627), (0.84873950481414795, 0.38431373238563538, 0.38431373238563538), (0.85294115543365479, 0.40000000596046448, 0.40000000596046448), (0.8571428656578064, 0.4117647111415863, 0.4117647111415863), (0.86134451627731323, 0.42745098471641541, 0.42745098471641541), (0.86554622650146484, 0.44313725829124451, 0.44313725829124451), (0.86974787712097168, 0.45882353186607361, 0.45882353186607361), (0.87394958734512329, 0.47450980544090271, 0.47450980544090271), (0.87815123796463013, 0.49019607901573181, 0.49019607901573181), (0.88235294818878174, 0.5215686559677124, 0.5215686559677124), (0.88655459880828857, 0.5372549295425415, 0.5372549295425415), (0.89075630903244019, 0.55294120311737061, 0.55294120311737061), (0.89495795965194702, 0.56862747669219971, 0.56862747669219971), (0.89915966987609863, 0.58431375026702881, 0.58431375026702881), (0.90336132049560547, 0.60000002384185791, 0.60000002384185791), (0.90756303071975708, 0.61176472902297974, 0.61176472902297974), (0.91176468133926392, 0.62745100259780884, 0.62745100259780884), (0.91596639156341553, 0.64313727617263794, 0.64313727617263794), (0.92016804218292236, 0.65882354974746704, 0.65882354974746704), (0.92436975240707397, 0.67450982332229614, 0.67450982332229614), (0.92857140302658081, 0.69019609689712524, 0.69019609689712524), (0.93277311325073242, 0.70588237047195435, 0.70588237047195435), (0.93697476387023926, 0.72156864404678345, 0.72156864404678345), (0.94117647409439087, 0.73725491762161255, 0.73725491762161255), (0.94537812471389771, 0.75294119119644165, 0.75294119119644165), (0.94957983493804932, 0.76862746477127075, 0.76862746477127075), (0.95378148555755615, 0.78431373834609985, 0.78431373834609985), (0.95798319578170776, 0.80000001192092896, 0.80000001192092896), (0.9621848464012146, 0.81176471710205078, 0.81176471710205078), (0.96638655662536621, 0.84313726425170898, 0.84313726425170898), (0.97058820724487305, 0.85882353782653809, 0.85882353782653809), (0.97478991746902466, 0.87450981140136719, 0.87450981140136719), (0.97899156808853149, 0.89019608497619629, 0.89019608497619629), (0.98319327831268311, 0.90588235855102539, 0.90588235855102539), (0.98739492893218994, 0.92156863212585449, 0.92156863212585449), (0.99159663915634155, 0.93725490570068359, 0.93725490570068359), (0.99579828977584839, 0.9529411792755127, 0.9529411792755127), (1.0, 0.9686274528503418, 0.9686274528503418)], ) return ColorMapper.from_segment_map(_data, range=range, **traits) def gist_ncar(range, **traits): """ Generator for the 'gist_ncar' colormap from GIST. """ _data = dict( red = [(0.0, 0.0, 0.0), (0.0050505050458014011, 0.0, 0.0), (0.010101010091602802, 0.0, 0.0), (0.015151515603065491, 0.0, 0.0), (0.020202020183205605, 0.0, 0.0), (0.025252524763345718, 0.0, 0.0), (0.030303031206130981, 0.0, 0.0), (0.035353533923625946, 0.0, 0.0), (0.040404040366411209, 0.0, 0.0), (0.045454546809196472, 0.0, 0.0), (0.050505049526691437, 0.0, 0.0), (0.0555555559694767, 0.0, 0.0), (0.060606062412261963, 0.0, 0.0), (0.065656565129756927, 0.0, 0.0), (0.070707067847251892, 0.0, 0.0), (0.075757578015327454, 0.0, 0.0), (0.080808080732822418, 0.0, 0.0), (0.085858583450317383, 0.0, 0.0), (0.090909093618392944, 0.0, 0.0), (0.095959596335887909, 0.0, 0.0), (0.10101009905338287, 0.0, 0.0), (0.10606060922145844, 0.0, 0.0), (0.1111111119389534, 0.0, 0.0), (0.11616161465644836, 0.0, 0.0), (0.12121212482452393, 0.0, 0.0), (0.12626262009143829, 0.0, 0.0), (0.13131313025951385, 0.0, 0.0), (0.13636364042758942, 0.0, 0.0), (0.14141413569450378, 0.0, 0.0), (0.14646464586257935, 0.0, 0.0), (0.15151515603065491, 0.0, 0.0), (0.15656565129756927, 0.0, 0.0), (0.16161616146564484, 0.0, 0.0), (0.1666666716337204, 0.0, 0.0), (0.17171716690063477, 0.0, 0.0), (0.17676767706871033, 0.0, 0.0), (0.18181818723678589, 0.0, 0.0), (0.18686868250370026, 0.0, 0.0), (0.19191919267177582, 0.0, 0.0), (0.19696970283985138, 0.0, 0.0), (0.20202019810676575, 0.0, 0.0), (0.20707070827484131, 0.0, 0.0), (0.21212121844291687, 0.0, 0.0), (0.21717171370983124, 0.0, 0.0), (0.2222222238779068, 0.0, 0.0), (0.22727273404598236, 0.0, 0.0), (0.23232322931289673, 0.0, 0.0), (0.23737373948097229, 0.0, 0.0), (0.24242424964904785, 0.0, 0.0), (0.24747474491596222, 0.0, 0.0), (0.25252524018287659, 0.0, 0.0), (0.25757575035095215, 0.0, 0.0), (0.26262626051902771, 0.0, 0.0), (0.26767677068710327, 0.0, 0.0), (0.27272728085517883, 0.0, 0.0), (0.27777779102325439, 0.0, 0.0), (0.28282827138900757, 0.0, 0.0), (0.28787878155708313, 0.0, 0.0), (0.29292929172515869, 0.0, 0.0), (0.29797980189323425, 0.0, 0.0), (0.30303031206130981, 0.0, 0.0), (0.30808082222938538, 0.0, 0.0), (0.31313130259513855, 0.0, 0.0), (0.31818181276321411, 0.0039215688593685627, 0.0039215688593685627), (0.32323232293128967, 0.043137256056070328, 0.043137256056070328), (0.32828283309936523, 0.08235294371843338, 0.08235294371843338), (0.3333333432674408, 0.11764705926179886, 0.11764705926179886), (0.33838382363319397, 0.15686275064945221, 0.15686275064945221), (0.34343433380126953, 0.19607843458652496, 0.19607843458652496), (0.34848484396934509, 0.23137255012989044, 0.23137255012989044), (0.35353535413742065, 0.27058824896812439, 0.27058824896812439), (0.35858586430549622, 0.30980393290519714, 0.30980393290519714), (0.36363637447357178, 0.3490196168422699, 0.3490196168422699), (0.36868685483932495, 0.38431373238563538, 0.38431373238563538), (0.37373736500740051, 0.40392157435417175, 0.40392157435417175), (0.37878787517547607, 0.41568627953529358, 0.41568627953529358), (0.38383838534355164, 0.42352941632270813, 0.42352941632270813), (0.3888888955116272, 0.43137255311012268, 0.43137255311012268), (0.39393940567970276, 0.44313725829124451, 0.44313725829124451), (0.39898988604545593, 0.45098039507865906, 0.45098039507865906), (0.40404039621353149, 0.45882353186607361, 0.45882353186607361), (0.40909090638160706, 0.47058823704719543, 0.47058823704719543), (0.41414141654968262, 0.47843137383460999, 0.47843137383460999), (0.41919192671775818, 0.49019607901573181, 0.49019607901573181), (0.42424243688583374, 0.50196081399917603, 0.50196081399917603), (0.42929291725158691, 0.52549022436141968, 0.52549022436141968), (0.43434342741966248, 0.54901963472366333, 0.54901963472366333), (0.43939393758773804, 0.57254904508590698, 0.57254904508590698), (0.4444444477558136, 0.60000002384185791, 0.60000002384185791), (0.44949495792388916, 0.62352943420410156, 0.62352943420410156), (0.45454546809196472, 0.64705884456634521, 0.64705884456634521), (0.4595959484577179, 0.67058825492858887, 0.67058825492858887), (0.46464645862579346, 0.69411766529083252, 0.69411766529083252), (0.46969696879386902, 0.72156864404678345, 0.72156864404678345), (0.47474747896194458, 0.7450980544090271, 0.7450980544090271), (0.47979798913002014, 0.76862746477127075, 0.76862746477127075), (0.4848484992980957, 0.7921568751335144, 0.7921568751335144), (0.48989897966384888, 0.81568628549575806, 0.81568628549575806), (0.49494948983192444, 0.83921569585800171, 0.83921569585800171), (0.5, 0.86274510622024536, 0.86274510622024536), (0.50505048036575317, 0.88627451658248901, 0.88627451658248901), (0.51010102033615112, 0.90980392694473267, 0.90980392694473267), (0.5151515007019043, 0.93333333730697632, 0.93333333730697632), (0.52020204067230225, 0.95686274766921997, 0.95686274766921997), (0.52525252103805542, 0.98039215803146362, 0.98039215803146362), (0.53030300140380859, 1.0, 1.0), (0.53535354137420654, 1.0, 1.0), (0.54040402173995972, 1.0, 1.0), (0.54545456171035767, 1.0, 1.0), (0.55050504207611084, 1.0, 1.0), (0.55555558204650879, 1.0, 1.0), (0.56060606241226196, 1.0, 1.0), (0.56565654277801514, 1.0, 1.0), (0.57070708274841309, 1.0, 1.0), (0.57575756311416626, 1.0, 1.0), (0.58080810308456421, 1.0, 1.0), (0.58585858345031738, 1.0, 1.0), (0.59090906381607056, 1.0, 1.0), (0.59595960378646851, 1.0, 1.0), (0.60101008415222168, 1.0, 1.0), (0.60606062412261963, 1.0, 1.0), (0.6111111044883728, 1.0, 1.0), (0.61616164445877075, 1.0, 1.0), (0.62121212482452393, 1.0, 1.0), (0.6262626051902771, 1.0, 1.0), (0.63131314516067505, 1.0, 1.0), (0.63636362552642822, 1.0, 1.0), (0.64141416549682617, 1.0, 1.0), (0.64646464586257935, 1.0, 1.0), (0.65151512622833252, 1.0, 1.0), (0.65656566619873047, 1.0, 1.0), (0.66161614656448364, 1.0, 1.0), (0.66666668653488159, 1.0, 1.0), (0.67171716690063477, 1.0, 1.0), (0.67676764726638794, 1.0, 1.0), (0.68181818723678589, 1.0, 1.0), (0.68686866760253906, 1.0, 1.0), (0.69191920757293701, 1.0, 1.0), (0.69696968793869019, 1.0, 1.0), (0.70202022790908813, 1.0, 1.0), (0.70707070827484131, 1.0, 1.0), (0.71212118864059448, 1.0, 1.0), (0.71717172861099243, 1.0, 1.0), (0.72222220897674561, 1.0, 1.0), (0.72727274894714355, 1.0, 1.0), (0.73232322931289673, 1.0, 1.0), (0.7373737096786499, 1.0, 1.0), (0.74242424964904785, 1.0, 1.0), (0.74747473001480103, 1.0, 1.0), (0.75252526998519897, 1.0, 1.0), (0.75757575035095215, 1.0, 1.0), (0.7626262903213501, 1.0, 1.0), (0.76767677068710327, 1.0, 1.0), (0.77272725105285645, 1.0, 1.0), (0.77777779102325439, 1.0, 1.0), (0.78282827138900757, 1.0, 1.0), (0.78787881135940552, 1.0, 1.0), (0.79292929172515869, 1.0, 1.0), (0.79797977209091187, 0.96470588445663452, 0.96470588445663452), (0.80303031206130981, 0.92549020051956177, 0.92549020051956177), (0.80808079242706299, 0.89019608497619629, 0.89019608497619629), (0.81313133239746094, 0.85098040103912354, 0.85098040103912354), (0.81818181276321411, 0.81568628549575806, 0.81568628549575806), (0.82323235273361206, 0.7764706015586853, 0.7764706015586853), (0.82828283309936523, 0.74117648601531982, 0.74117648601531982), (0.83333331346511841, 0.70196080207824707, 0.70196080207824707), (0.83838385343551636, 0.66666668653488159, 0.66666668653488159), (0.84343433380126953, 0.62745100259780884, 0.62745100259780884), (0.84848487377166748, 0.61960786581039429, 0.61960786581039429), (0.85353535413742065, 0.65098041296005249, 0.65098041296005249), (0.85858583450317383, 0.68235296010971069, 0.68235296010971069), (0.86363637447357178, 0.7137255072593689, 0.7137255072593689), (0.86868685483932495, 0.7450980544090271, 0.7450980544090271), (0.8737373948097229, 0.77254903316497803, 0.77254903316497803), (0.87878787517547607, 0.80392158031463623, 0.80392158031463623), (0.88383835554122925, 0.83529412746429443, 0.83529412746429443), (0.8888888955116272, 0.86666667461395264, 0.86666667461395264), (0.89393937587738037, 0.89803922176361084, 0.89803922176361084), (0.89898991584777832, 0.92941176891326904, 0.92941176891326904), (0.90404039621353149, 0.93333333730697632, 0.93333333730697632), (0.90909093618392944, 0.93725490570068359, 0.93725490570068359), (0.91414141654968262, 0.93725490570068359, 0.93725490570068359), (0.91919189691543579, 0.94117647409439087, 0.94117647409439087), (0.92424243688583374, 0.94509804248809814, 0.94509804248809814), (0.92929291725158691, 0.94509804248809814, 0.94509804248809814), (0.93434345722198486, 0.94901961088180542, 0.94901961088180542), (0.93939393758773804, 0.9529411792755127, 0.9529411792755127), (0.94444441795349121, 0.9529411792755127, 0.9529411792755127), (0.94949495792388916, 0.95686274766921997, 0.95686274766921997), (0.95454543828964233, 0.96078431606292725, 0.96078431606292725), (0.95959597826004028, 0.96470588445663452, 0.96470588445663452), (0.96464645862579346, 0.9686274528503418, 0.9686274528503418), (0.96969699859619141, 0.97254902124404907, 0.97254902124404907), (0.97474747896194458, 0.97647058963775635, 0.97647058963775635), (0.97979795932769775, 0.98039215803146362, 0.98039215803146362), (0.9848484992980957, 0.9843137264251709, 0.9843137264251709), (0.98989897966384888, 0.98823529481887817, 0.98823529481887817), (0.99494951963424683, 0.99215686321258545, 0.99215686321258545), (1.0, 0.99607843160629272, 0.99607843160629272)], green = [(0.0, 0.0, 0.0), (0.0050505050458014011, 0.035294119268655777, 0.035294119268655777), (0.010101010091602802, 0.074509806931018829, 0.074509806931018829), (0.015151515603065491, 0.10980392247438431, 0.10980392247438431), (0.020202020183205605, 0.14901961386203766, 0.14901961386203766), (0.025252524763345718, 0.18431372940540314, 0.18431372940540314), (0.030303031206130981, 0.22352941334247589, 0.22352941334247589), (0.035353533923625946, 0.25882354378700256, 0.25882354378700256), (0.040404040366411209, 0.29803922772407532, 0.29803922772407532), (0.045454546809196472, 0.3333333432674408, 0.3333333432674408), (0.050505049526691437, 0.37254902720451355, 0.37254902720451355), (0.0555555559694767, 0.36862745881080627, 0.36862745881080627), (0.060606062412261963, 0.3333333432674408, 0.3333333432674408), (0.065656565129756927, 0.29411765933036804, 0.29411765933036804), (0.070707067847251892, 0.25882354378700256, 0.25882354378700256), (0.075757578015327454, 0.21960784494876862, 0.21960784494876862), (0.080808080732822418, 0.18431372940540314, 0.18431372940540314), (0.085858583450317383, 0.14509804546833038, 0.14509804546833038), (0.090909093618392944, 0.10980392247438431, 0.10980392247438431), (0.095959596335887909, 0.070588238537311554, 0.070588238537311554), (0.10101009905338287, 0.035294119268655777, 0.035294119268655777), (0.10606060922145844, 0.0, 0.0), (0.1111111119389534, 0.074509806931018829, 0.074509806931018829), (0.11616161465644836, 0.14509804546833038, 0.14509804546833038), (0.12121212482452393, 0.21568627655506134, 0.21568627655506134), (0.12626262009143829, 0.28627452254295349, 0.28627452254295349), (0.13131313025951385, 0.36078432202339172, 0.36078432202339172), (0.13636364042758942, 0.43137255311012268, 0.43137255311012268), (0.14141413569450378, 0.50196081399917603, 0.50196081399917603), (0.14646464586257935, 0.57254904508590698, 0.57254904508590698), (0.15151515603065491, 0.64705884456634521, 0.64705884456634521), (0.15656565129756927, 0.71764707565307617, 0.71764707565307617), (0.16161616146564484, 0.7607843279838562, 0.7607843279838562), (0.1666666716337204, 0.78431373834609985, 0.78431373834609985), (0.17171716690063477, 0.80784314870834351, 0.80784314870834351), (0.17676767706871033, 0.83137255907058716, 0.83137255907058716), (0.18181818723678589, 0.85490196943283081, 0.85490196943283081), (0.18686868250370026, 0.88235294818878174, 0.88235294818878174), (0.19191919267177582, 0.90588235855102539, 0.90588235855102539), (0.19696970283985138, 0.92941176891326904, 0.92941176891326904), (0.20202019810676575, 0.9529411792755127, 0.9529411792755127), (0.20707070827484131, 0.97647058963775635, 0.97647058963775635), (0.21212121844291687, 0.99607843160629272, 0.99607843160629272), (0.21717171370983124, 0.99607843160629272, 0.99607843160629272), (0.2222222238779068, 0.99215686321258545, 0.99215686321258545), (0.22727273404598236, 0.99215686321258545, 0.99215686321258545), (0.23232322931289673, 0.99215686321258545, 0.99215686321258545), (0.23737373948097229, 0.98823529481887817, 0.98823529481887817), (0.24242424964904785, 0.98823529481887817, 0.98823529481887817), (0.24747474491596222, 0.9843137264251709, 0.9843137264251709), (0.25252524018287659, 0.9843137264251709, 0.9843137264251709), (0.25757575035095215, 0.98039215803146362, 0.98039215803146362), (0.26262626051902771, 0.98039215803146362, 0.98039215803146362), (0.26767677068710327, 0.98039215803146362, 0.98039215803146362), (0.27272728085517883, 0.98039215803146362, 0.98039215803146362), (0.27777779102325439, 0.9843137264251709, 0.9843137264251709), (0.28282827138900757, 0.9843137264251709, 0.9843137264251709), (0.28787878155708313, 0.98823529481887817, 0.98823529481887817), (0.29292929172515869, 0.98823529481887817, 0.98823529481887817), (0.29797980189323425, 0.99215686321258545, 0.99215686321258545), (0.30303031206130981, 0.99215686321258545, 0.99215686321258545), (0.30808082222938538, 0.99607843160629272, 0.99607843160629272), (0.31313130259513855, 0.99607843160629272, 0.99607843160629272), (0.31818181276321411, 0.99607843160629272, 0.99607843160629272), (0.32323232293128967, 0.97647058963775635, 0.97647058963775635), (0.32828283309936523, 0.95686274766921997, 0.95686274766921997), (0.3333333432674408, 0.93725490570068359, 0.93725490570068359), (0.33838382363319397, 0.92156863212585449, 0.92156863212585449), (0.34343433380126953, 0.90196079015731812, 0.90196079015731812), (0.34848484396934509, 0.88235294818878174, 0.88235294818878174), (0.35353535413742065, 0.86274510622024536, 0.86274510622024536), (0.35858586430549622, 0.84705883264541626, 0.84705883264541626), (0.36363637447357178, 0.82745099067687988, 0.82745099067687988), (0.36868685483932495, 0.80784314870834351, 0.80784314870834351), (0.37373736500740051, 0.81568628549575806, 0.81568628549575806), (0.37878787517547607, 0.83529412746429443, 0.83529412746429443), (0.38383838534355164, 0.85098040103912354, 0.85098040103912354), (0.3888888955116272, 0.87058824300765991, 0.87058824300765991), (0.39393940567970276, 0.89019608497619629, 0.89019608497619629), (0.39898988604545593, 0.90980392694473267, 0.90980392694473267), (0.40404039621353149, 0.92549020051956177, 0.92549020051956177), (0.40909090638160706, 0.94509804248809814, 0.94509804248809814), (0.41414141654968262, 0.96470588445663452, 0.96470588445663452), (0.41919192671775818, 0.9843137264251709, 0.9843137264251709), (0.42424243688583374, 1.0, 1.0), (0.42929291725158691, 1.0, 1.0), (0.43434342741966248, 1.0, 1.0), (0.43939393758773804, 1.0, 1.0), (0.4444444477558136, 1.0, 1.0), (0.44949495792388916, 1.0, 1.0), (0.45454546809196472, 1.0, 1.0), (0.4595959484577179, 1.0, 1.0), (0.46464645862579346, 1.0, 1.0), (0.46969696879386902, 1.0, 1.0), (0.47474747896194458, 1.0, 1.0), (0.47979798913002014, 1.0, 1.0), (0.4848484992980957, 1.0, 1.0), (0.48989897966384888, 1.0, 1.0), (0.49494948983192444, 1.0, 1.0), (0.5, 1.0, 1.0), (0.50505048036575317, 1.0, 1.0), (0.51010102033615112, 1.0, 1.0), (0.5151515007019043, 1.0, 1.0), (0.52020204067230225, 1.0, 1.0), (0.52525252103805542, 1.0, 1.0), (0.53030300140380859, 0.99215686321258545, 0.99215686321258545), (0.53535354137420654, 0.98039215803146362, 0.98039215803146362), (0.54040402173995972, 0.96470588445663452, 0.96470588445663452), (0.54545456171035767, 0.94901961088180542, 0.94901961088180542), (0.55050504207611084, 0.93333333730697632, 0.93333333730697632), (0.55555558204650879, 0.91764706373214722, 0.91764706373214722), (0.56060606241226196, 0.90588235855102539, 0.90588235855102539), (0.56565654277801514, 0.89019608497619629, 0.89019608497619629), (0.57070708274841309, 0.87450981140136719, 0.87450981140136719), (0.57575756311416626, 0.85882353782653809, 0.85882353782653809), (0.58080810308456421, 0.84313726425170898, 0.84313726425170898), (0.58585858345031738, 0.83137255907058716, 0.83137255907058716), (0.59090906381607056, 0.81960785388946533, 0.81960785388946533), (0.59595960378646851, 0.81176471710205078, 0.81176471710205078), (0.60101008415222168, 0.80000001192092896, 0.80000001192092896), (0.60606062412261963, 0.78823530673980713, 0.78823530673980713), (0.6111111044883728, 0.7764706015586853, 0.7764706015586853), (0.61616164445877075, 0.76470589637756348, 0.76470589637756348), (0.62121212482452393, 0.75294119119644165, 0.75294119119644165), (0.6262626051902771, 0.74117648601531982, 0.74117648601531982), (0.63131314516067505, 0.729411780834198, 0.729411780834198), (0.63636362552642822, 0.70980393886566162, 0.70980393886566162), (0.64141416549682617, 0.66666668653488159, 0.66666668653488159), (0.64646464586257935, 0.62352943420410156, 0.62352943420410156), (0.65151512622833252, 0.58039218187332153, 0.58039218187332153), (0.65656566619873047, 0.5372549295425415, 0.5372549295425415), (0.66161614656448364, 0.49411764740943909, 0.49411764740943909), (0.66666668653488159, 0.45098039507865906, 0.45098039507865906), (0.67171716690063477, 0.40392157435417175, 0.40392157435417175), (0.67676764726638794, 0.36078432202339172, 0.36078432202339172), (0.68181818723678589, 0.31764706969261169, 0.31764706969261169), (0.68686866760253906, 0.27450981736183167, 0.27450981736183167), (0.69191920757293701, 0.24705882370471954, 0.24705882370471954), (0.69696968793869019, 0.21960784494876862, 0.21960784494876862), (0.70202022790908813, 0.19607843458652496, 0.19607843458652496), (0.70707070827484131, 0.16862745583057404, 0.16862745583057404), (0.71212118864059448, 0.14509804546833038, 0.14509804546833038), (0.71717172861099243, 0.11764705926179886, 0.11764705926179886), (0.72222220897674561, 0.090196080505847931, 0.090196080505847931), (0.72727274894714355, 0.066666670143604279, 0.066666670143604279), (0.73232322931289673, 0.039215687662363052, 0.039215687662363052), (0.7373737096786499, 0.015686275437474251, 0.015686275437474251), (0.74242424964904785, 0.0, 0.0), (0.74747473001480103, 0.0, 0.0), (0.75252526998519897, 0.0, 0.0), (0.75757575035095215, 0.0, 0.0), (0.7626262903213501, 0.0, 0.0), (0.76767677068710327, 0.0, 0.0), (0.77272725105285645, 0.0, 0.0), (0.77777779102325439, 0.0, 0.0), (0.78282827138900757, 0.0, 0.0), (0.78787881135940552, 0.0, 0.0), (0.79292929172515869, 0.0, 0.0), (0.79797977209091187, 0.015686275437474251, 0.015686275437474251), (0.80303031206130981, 0.031372550874948502, 0.031372550874948502), (0.80808079242706299, 0.050980392843484879, 0.050980392843484879), (0.81313133239746094, 0.066666670143604279, 0.066666670143604279), (0.81818181276321411, 0.086274512112140656, 0.086274512112140656), (0.82323235273361206, 0.10588235408067703, 0.10588235408067703), (0.82828283309936523, 0.12156862765550613, 0.12156862765550613), (0.83333331346511841, 0.14117647707462311, 0.14117647707462311), (0.83838385343551636, 0.15686275064945221, 0.15686275064945221), (0.84343433380126953, 0.17647059261798859, 0.17647059261798859), (0.84848487377166748, 0.20000000298023224, 0.20000000298023224), (0.85353535413742065, 0.23137255012989044, 0.23137255012989044), (0.85858583450317383, 0.25882354378700256, 0.25882354378700256), (0.86363637447357178, 0.29019609093666077, 0.29019609093666077), (0.86868685483932495, 0.32156863808631897, 0.32156863808631897), (0.8737373948097229, 0.35294118523597717, 0.35294118523597717), (0.87878787517547607, 0.38431373238563538, 0.38431373238563538), (0.88383835554122925, 0.41568627953529358, 0.41568627953529358), (0.8888888955116272, 0.44313725829124451, 0.44313725829124451), (0.89393937587738037, 0.47450980544090271, 0.47450980544090271), (0.89898991584777832, 0.5058823823928833, 0.5058823823928833), (0.90404039621353149, 0.52941179275512695, 0.52941179275512695), (0.90909093618392944, 0.55294120311737061, 0.55294120311737061), (0.91414141654968262, 0.57254904508590698, 0.57254904508590698), (0.91919189691543579, 0.59607845544815063, 0.59607845544815063), (0.92424243688583374, 0.61960786581039429, 0.61960786581039429), (0.92929291725158691, 0.64313727617263794, 0.64313727617263794), (0.93434345722198486, 0.66274511814117432, 0.66274511814117432), (0.93939393758773804, 0.68627452850341797, 0.68627452850341797), (0.94444441795349121, 0.70980393886566162, 0.70980393886566162), (0.94949495792388916, 0.729411780834198, 0.729411780834198), (0.95454543828964233, 0.75294119119644165, 0.75294119119644165), (0.95959597826004028, 0.78039216995239258, 0.78039216995239258), (0.96464645862579346, 0.80392158031463623, 0.80392158031463623), (0.96969699859619141, 0.82745099067687988, 0.82745099067687988), (0.97474747896194458, 0.85098040103912354, 0.85098040103912354), (0.97979795932769775, 0.87450981140136719, 0.87450981140136719), (0.9848484992980957, 0.90196079015731812, 0.90196079015731812), (0.98989897966384888, 0.92549020051956177, 0.92549020051956177), (0.99494951963424683, 0.94901961088180542, 0.94901961088180542), (1.0, 0.97254902124404907, 0.97254902124404907)], blue = [(0.0, 0.50196081399917603, 0.50196081399917603), (0.0050505050458014011, 0.45098039507865906, 0.45098039507865906), (0.010101010091602802, 0.40392157435417175, 0.40392157435417175), (0.015151515603065491, 0.35686275362968445, 0.35686275362968445), (0.020202020183205605, 0.30980393290519714, 0.30980393290519714), (0.025252524763345718, 0.25882354378700256, 0.25882354378700256), (0.030303031206130981, 0.21176470816135406, 0.21176470816135406), (0.035353533923625946, 0.16470588743686676, 0.16470588743686676), (0.040404040366411209, 0.11764705926179886, 0.11764705926179886), (0.045454546809196472, 0.070588238537311554, 0.070588238537311554), (0.050505049526691437, 0.019607843831181526, 0.019607843831181526), (0.0555555559694767, 0.047058824449777603, 0.047058824449777603), (0.060606062412261963, 0.14509804546833038, 0.14509804546833038), (0.065656565129756927, 0.23921568691730499, 0.23921568691730499), (0.070707067847251892, 0.3333333432674408, 0.3333333432674408), (0.075757578015327454, 0.43137255311012268, 0.43137255311012268), (0.080808080732822418, 0.52549022436141968, 0.52549022436141968), (0.085858583450317383, 0.61960786581039429, 0.61960786581039429), (0.090909093618392944, 0.71764707565307617, 0.71764707565307617), (0.095959596335887909, 0.81176471710205078, 0.81176471710205078), (0.10101009905338287, 0.90588235855102539, 0.90588235855102539), (0.10606060922145844, 1.0, 1.0), (0.1111111119389534, 1.0, 1.0), (0.11616161465644836, 1.0, 1.0), (0.12121212482452393, 1.0, 1.0), (0.12626262009143829, 1.0, 1.0), (0.13131313025951385, 1.0, 1.0), (0.13636364042758942, 1.0, 1.0), (0.14141413569450378, 1.0, 1.0), (0.14646464586257935, 1.0, 1.0), (0.15151515603065491, 1.0, 1.0), (0.15656565129756927, 1.0, 1.0), (0.16161616146564484, 1.0, 1.0), (0.1666666716337204, 1.0, 1.0), (0.17171716690063477, 1.0, 1.0), (0.17676767706871033, 1.0, 1.0), (0.18181818723678589, 1.0, 1.0), (0.18686868250370026, 1.0, 1.0), (0.19191919267177582, 1.0, 1.0), (0.19696970283985138, 1.0, 1.0), (0.20202019810676575, 1.0, 1.0), (0.20707070827484131, 1.0, 1.0), (0.21212121844291687, 0.99215686321258545, 0.99215686321258545), (0.21717171370983124, 0.95686274766921997, 0.95686274766921997), (0.2222222238779068, 0.91764706373214722, 0.91764706373214722), (0.22727273404598236, 0.88235294818878174, 0.88235294818878174), (0.23232322931289673, 0.84313726425170898, 0.84313726425170898), (0.23737373948097229, 0.80392158031463623, 0.80392158031463623), (0.24242424964904785, 0.76862746477127075, 0.76862746477127075), (0.24747474491596222, 0.729411780834198, 0.729411780834198), (0.25252524018287659, 0.69019609689712524, 0.69019609689712524), (0.25757575035095215, 0.65490198135375977, 0.65490198135375977), (0.26262626051902771, 0.61568629741668701, 0.61568629741668701), (0.26767677068710327, 0.56470590829849243, 0.56470590829849243), (0.27272728085517883, 0.50980395078659058, 0.50980395078659058), (0.27777779102325439, 0.45098039507865906, 0.45098039507865906), (0.28282827138900757, 0.39215686917304993, 0.39215686917304993), (0.28787878155708313, 0.3333333432674408, 0.3333333432674408), (0.29292929172515869, 0.27843138575553894, 0.27843138575553894), (0.29797980189323425, 0.21960784494876862, 0.21960784494876862), (0.30303031206130981, 0.16078431904315948, 0.16078431904315948), (0.30808082222938538, 0.10588235408067703, 0.10588235408067703), (0.31313130259513855, 0.047058824449777603, 0.047058824449777603), (0.31818181276321411, 0.0, 0.0), (0.32323232293128967, 0.0, 0.0), (0.32828283309936523, 0.0, 0.0), (0.3333333432674408, 0.0, 0.0), (0.33838382363319397, 0.0, 0.0), (0.34343433380126953, 0.0, 0.0), (0.34848484396934509, 0.0, 0.0), (0.35353535413742065, 0.0, 0.0), (0.35858586430549622, 0.0, 0.0), (0.36363637447357178, 0.0, 0.0), (0.36868685483932495, 0.0, 0.0), (0.37373736500740051, 0.0, 0.0), (0.37878787517547607, 0.0, 0.0), (0.38383838534355164, 0.0, 0.0), (0.3888888955116272, 0.0, 0.0), (0.39393940567970276, 0.0, 0.0), (0.39898988604545593, 0.0, 0.0), (0.40404039621353149, 0.0, 0.0), (0.40909090638160706, 0.0, 0.0), (0.41414141654968262, 0.0, 0.0), (0.41919192671775818, 0.0, 0.0), (0.42424243688583374, 0.0039215688593685627, 0.0039215688593685627), (0.42929291725158691, 0.027450980618596077, 0.027450980618596077), (0.43434342741966248, 0.050980392843484879, 0.050980392843484879), (0.43939393758773804, 0.074509806931018829, 0.074509806931018829), (0.4444444477558136, 0.094117648899555206, 0.094117648899555206), (0.44949495792388916, 0.11764705926179886, 0.11764705926179886), (0.45454546809196472, 0.14117647707462311, 0.14117647707462311), (0.4595959484577179, 0.16470588743686676, 0.16470588743686676), (0.46464645862579346, 0.18823529779911041, 0.18823529779911041), (0.46969696879386902, 0.21176470816135406, 0.21176470816135406), (0.47474747896194458, 0.23529411852359772, 0.23529411852359772), (0.47979798913002014, 0.22352941334247589, 0.22352941334247589), (0.4848484992980957, 0.20000000298023224, 0.20000000298023224), (0.48989897966384888, 0.17647059261798859, 0.17647059261798859), (0.49494948983192444, 0.15294118225574493, 0.15294118225574493), (0.5, 0.12941177189350128, 0.12941177189350128), (0.50505048036575317, 0.10980392247438431, 0.10980392247438431), (0.51010102033615112, 0.086274512112140656, 0.086274512112140656), (0.5151515007019043, 0.062745101749897003, 0.062745101749897003), (0.52020204067230225, 0.039215687662363052, 0.039215687662363052), (0.52525252103805542, 0.015686275437474251, 0.015686275437474251), (0.53030300140380859, 0.0, 0.0), (0.53535354137420654, 0.0, 0.0), (0.54040402173995972, 0.0, 0.0), (0.54545456171035767, 0.0, 0.0), (0.55050504207611084, 0.0, 0.0), (0.55555558204650879, 0.0, 0.0), (0.56060606241226196, 0.0, 0.0), (0.56565654277801514, 0.0, 0.0), (0.57070708274841309, 0.0, 0.0), (0.57575756311416626, 0.0, 0.0), (0.58080810308456421, 0.0, 0.0), (0.58585858345031738, 0.0039215688593685627, 0.0039215688593685627), (0.59090906381607056, 0.0078431377187371254, 0.0078431377187371254), (0.59595960378646851, 0.011764706112444401, 0.011764706112444401), (0.60101008415222168, 0.019607843831181526, 0.019607843831181526), (0.60606062412261963, 0.023529412224888802, 0.023529412224888802), (0.6111111044883728, 0.031372550874948502, 0.031372550874948502), (0.61616164445877075, 0.035294119268655777, 0.035294119268655777), (0.62121212482452393, 0.043137256056070328, 0.043137256056070328), (0.6262626051902771, 0.047058824449777603, 0.047058824449777603), (0.63131314516067505, 0.054901961237192154, 0.054901961237192154), (0.63636362552642822, 0.054901961237192154, 0.054901961237192154), (0.64141416549682617, 0.050980392843484879, 0.050980392843484879), (0.64646464586257935, 0.043137256056070328, 0.043137256056070328), (0.65151512622833252, 0.039215687662363052, 0.039215687662363052), (0.65656566619873047, 0.031372550874948502, 0.031372550874948502), (0.66161614656448364, 0.027450980618596077, 0.027450980618596077), (0.66666668653488159, 0.019607843831181526, 0.019607843831181526), (0.67171716690063477, 0.015686275437474251, 0.015686275437474251), (0.67676764726638794, 0.011764706112444401, 0.011764706112444401), (0.68181818723678589, 0.0039215688593685627, 0.0039215688593685627), (0.68686866760253906, 0.0, 0.0), (0.69191920757293701, 0.0, 0.0), (0.69696968793869019, 0.0, 0.0), (0.70202022790908813, 0.0, 0.0), (0.70707070827484131, 0.0, 0.0), (0.71212118864059448, 0.0, 0.0), (0.71717172861099243, 0.0, 0.0), (0.72222220897674561, 0.0, 0.0), (0.72727274894714355, 0.0, 0.0), (0.73232322931289673, 0.0, 0.0), (0.7373737096786499, 0.0, 0.0), (0.74242424964904785, 0.031372550874948502, 0.031372550874948502), (0.74747473001480103, 0.12941177189350128, 0.12941177189350128), (0.75252526998519897, 0.22352941334247589, 0.22352941334247589), (0.75757575035095215, 0.32156863808631897, 0.32156863808631897), (0.7626262903213501, 0.41568627953529358, 0.41568627953529358), (0.76767677068710327, 0.50980395078659058, 0.50980395078659058), (0.77272725105285645, 0.60784316062927246, 0.60784316062927246), (0.77777779102325439, 0.70196080207824707, 0.70196080207824707), (0.78282827138900757, 0.79607844352722168, 0.79607844352722168), (0.78787881135940552, 0.89411765336990356, 0.89411765336990356), (0.79292929172515869, 0.98823529481887817, 0.98823529481887817), (0.79797977209091187, 1.0, 1.0), (0.80303031206130981, 1.0, 1.0), (0.80808079242706299, 1.0, 1.0), (0.81313133239746094, 1.0, 1.0), (0.81818181276321411, 1.0, 1.0), (0.82323235273361206, 1.0, 1.0), (0.82828283309936523, 1.0, 1.0), (0.83333331346511841, 1.0, 1.0), (0.83838385343551636, 1.0, 1.0), (0.84343433380126953, 1.0, 1.0), (0.84848487377166748, 0.99607843160629272, 0.99607843160629272), (0.85353535413742065, 0.98823529481887817, 0.98823529481887817), (0.85858583450317383, 0.9843137264251709, 0.9843137264251709), (0.86363637447357178, 0.97647058963775635, 0.97647058963775635), (0.86868685483932495, 0.9686274528503418, 0.9686274528503418), (0.8737373948097229, 0.96470588445663452, 0.96470588445663452), (0.87878787517547607, 0.95686274766921997, 0.95686274766921997), (0.88383835554122925, 0.94901961088180542, 0.94901961088180542), (0.8888888955116272, 0.94509804248809814, 0.94509804248809814), (0.89393937587738037, 0.93725490570068359, 0.93725490570068359), (0.89898991584777832, 0.93333333730697632, 0.93333333730697632), (0.90404039621353149, 0.93333333730697632, 0.93333333730697632), (0.90909093618392944, 0.93725490570068359, 0.93725490570068359), (0.91414141654968262, 0.93725490570068359, 0.93725490570068359), (0.91919189691543579, 0.94117647409439087, 0.94117647409439087), (0.92424243688583374, 0.94509804248809814, 0.94509804248809814), (0.92929291725158691, 0.94509804248809814, 0.94509804248809814), (0.93434345722198486, 0.94901961088180542, 0.94901961088180542), (0.93939393758773804, 0.9529411792755127, 0.9529411792755127), (0.94444441795349121, 0.9529411792755127, 0.9529411792755127), (0.94949495792388916, 0.95686274766921997, 0.95686274766921997), (0.95454543828964233, 0.96078431606292725, 0.96078431606292725), (0.95959597826004028, 0.96470588445663452, 0.96470588445663452), (0.96464645862579346, 0.9686274528503418, 0.9686274528503418), (0.96969699859619141, 0.97254902124404907, 0.97254902124404907), (0.97474747896194458, 0.97647058963775635, 0.97647058963775635), (0.97979795932769775, 0.98039215803146362, 0.98039215803146362), (0.9848484992980957, 0.9843137264251709, 0.9843137264251709), (0.98989897966384888, 0.98823529481887817, 0.98823529481887817), (0.99494951963424683, 0.99215686321258545, 0.99215686321258545), (1.0, 0.99607843160629272, 0.99607843160629272)], ) return ColorMapper.from_segment_map(_data, range=range, **traits) def gist_rainbow(range, **traits): """ Generator for the 'gist_rainbow' colormap from GIST. """ _data = dict( red = [(0.0, 1.0, 1.0), (0.0042016808874905109, 1.0, 1.0), (0.0084033617749810219, 1.0, 1.0), (0.012605042196810246, 1.0, 1.0), (0.016806723549962044, 1.0, 1.0), (0.021008403971791267, 1.0, 1.0), (0.025210084393620491, 1.0, 1.0), (0.029411764815449715, 1.0, 1.0), (0.033613447099924088, 1.0, 1.0), (0.037815127521753311, 1.0, 1.0), (0.042016807943582535, 1.0, 1.0), (0.046218488365411758, 1.0, 1.0), (0.050420168787240982, 1.0, 1.0), (0.054621849209070206, 1.0, 1.0), (0.058823529630899429, 1.0, 1.0), (0.063025213778018951, 1.0, 1.0), (0.067226894199848175, 1.0, 1.0), (0.071428574621677399, 1.0, 1.0), (0.075630255043506622, 1.0, 1.0), (0.079831935465335846, 1.0, 1.0), (0.08403361588716507, 1.0, 1.0), (0.088235296308994293, 1.0, 1.0), (0.092436976730823517, 1.0, 1.0), (0.09663865715265274, 1.0, 1.0), (0.10084033757448196, 1.0, 1.0), (0.10504201799631119, 1.0, 1.0), (0.10924369841814041, 1.0, 1.0), (0.11344537883996964, 1.0, 1.0), (0.11764705926179886, 1.0, 1.0), (0.12184873968362808, 1.0, 1.0), (0.1260504275560379, 1.0, 1.0), (0.13025210797786713, 1.0, 1.0), (0.13445378839969635, 1.0, 1.0), (0.13865546882152557, 1.0, 1.0), (0.1428571492433548, 1.0, 1.0), (0.14705882966518402, 1.0, 1.0), (0.15126051008701324, 1.0, 1.0), (0.15546219050884247, 1.0, 1.0), (0.15966387093067169, 1.0, 1.0), (0.16386555135250092, 1.0, 1.0), (0.16806723177433014, 1.0, 1.0), (0.17226891219615936, 1.0, 1.0), (0.17647059261798859, 1.0, 1.0), (0.18067227303981781, 1.0, 1.0), (0.18487395346164703, 1.0, 1.0), (0.18907563388347626, 1.0, 1.0), (0.19327731430530548, 1.0, 1.0), (0.1974789947271347, 1.0, 1.0), (0.20168067514896393, 1.0, 1.0), (0.20588235557079315, 1.0, 1.0), (0.21008403599262238, 1.0, 1.0), (0.2142857164144516, 1.0, 1.0), (0.21848739683628082, 1.0, 1.0), (0.22268907725811005, 0.96078431606292725, 0.96078431606292725), (0.22689075767993927, 0.94117647409439087, 0.94117647409439087), (0.23109243810176849, 0.92156863212585449, 0.92156863212585449), (0.23529411852359772, 0.89803922176361084, 0.89803922176361084), (0.23949579894542694, 0.87843137979507446, 0.87843137979507446), (0.24369747936725616, 0.85882353782653809, 0.85882353782653809), (0.24789915978908539, 0.83529412746429443, 0.83529412746429443), (0.25210085511207581, 0.81568628549575806, 0.81568628549575806), (0.25630253553390503, 0.7921568751335144, 0.7921568751335144), (0.26050421595573425, 0.77254903316497803, 0.77254903316497803), (0.26470589637756348, 0.75294119119644165, 0.75294119119644165), (0.2689075767993927, 0.729411780834198, 0.729411780834198), (0.27310925722122192, 0.70980393886566162, 0.70980393886566162), (0.27731093764305115, 0.68627452850341797, 0.68627452850341797), (0.28151261806488037, 0.66666668653488159, 0.66666668653488159), (0.28571429848670959, 0.62352943420410156, 0.62352943420410156), (0.28991597890853882, 0.60392159223556519, 0.60392159223556519), (0.29411765933036804, 0.58431375026702881, 0.58431375026702881), (0.29831933975219727, 0.56078433990478516, 0.56078433990478516), (0.30252102017402649, 0.54117649793624878, 0.54117649793624878), (0.30672270059585571, 0.51764708757400513, 0.51764708757400513), (0.31092438101768494, 0.49803921580314636, 0.49803921580314636), (0.31512606143951416, 0.47843137383460999, 0.47843137383460999), (0.31932774186134338, 0.45490196347236633, 0.45490196347236633), (0.32352942228317261, 0.43529412150382996, 0.43529412150382996), (0.32773110270500183, 0.41568627953529358, 0.41568627953529358), (0.33193278312683105, 0.39215686917304993, 0.39215686917304993), (0.33613446354866028, 0.37254902720451355, 0.37254902720451355), (0.3403361439704895, 0.3490196168422699, 0.3490196168422699), (0.34453782439231873, 0.32941177487373352, 0.32941177487373352), (0.34873950481414795, 0.28627452254295349, 0.28627452254295349), (0.35294118523597717, 0.26666668057441711, 0.26666668057441711), (0.3571428656578064, 0.24705882370471954, 0.24705882370471954), (0.36134454607963562, 0.22352941334247589, 0.22352941334247589), (0.36554622650146484, 0.20392157137393951, 0.20392157137393951), (0.36974790692329407, 0.18039216101169586, 0.18039216101169586), (0.37394958734512329, 0.16078431904315948, 0.16078431904315948), (0.37815126776695251, 0.14117647707462311, 0.14117647707462311), (0.38235294818878174, 0.11764705926179886, 0.11764705926179886), (0.38655462861061096, 0.098039217293262482, 0.098039217293262482), (0.39075630903244019, 0.074509806931018829, 0.074509806931018829), (0.39495798945426941, 0.054901961237192154, 0.054901961237192154), (0.39915966987609863, 0.035294119268655777, 0.035294119268655777), (0.40336135029792786, 0.011764706112444401, 0.011764706112444401), (0.40756303071975708, 0.0, 0.0), (0.4117647111415863, 0.0, 0.0), (0.41596639156341553, 0.0, 0.0), (0.42016807198524475, 0.0, 0.0), (0.42436975240707397, 0.0, 0.0), (0.4285714328289032, 0.0, 0.0), (0.43277311325073242, 0.0, 0.0), (0.43697479367256165, 0.0, 0.0), (0.44117647409439087, 0.0, 0.0), (0.44537815451622009, 0.0, 0.0), (0.44957983493804932, 0.0, 0.0), (0.45378151535987854, 0.0, 0.0), (0.45798319578170776, 0.0, 0.0), (0.46218487620353699, 0.0, 0.0), (0.46638655662536621, 0.0, 0.0), (0.47058823704719543, 0.0, 0.0), (0.47478991746902466, 0.0, 0.0), (0.47899159789085388, 0.0, 0.0), (0.48319327831268311, 0.0, 0.0), (0.48739495873451233, 0.0, 0.0), (0.49159663915634155, 0.0, 0.0), (0.49579831957817078, 0.0, 0.0), (0.5, 0.0, 0.0), (0.50420171022415161, 0.0, 0.0), (0.50840336084365845, 0.0, 0.0), (0.51260507106781006, 0.0, 0.0), (0.51680672168731689, 0.0, 0.0), (0.52100843191146851, 0.0, 0.0), (0.52521008253097534, 0.0, 0.0), (0.52941179275512695, 0.0, 0.0), (0.53361344337463379, 0.0, 0.0), (0.5378151535987854, 0.0, 0.0), (0.54201680421829224, 0.0, 0.0), (0.54621851444244385, 0.0, 0.0), (0.55042016506195068, 0.0, 0.0), (0.55462187528610229, 0.0, 0.0), (0.55882352590560913, 0.0, 0.0), (0.56302523612976074, 0.0, 0.0), (0.56722688674926758, 0.0, 0.0), (0.57142859697341919, 0.0, 0.0), (0.57563024759292603, 0.0, 0.0), (0.57983195781707764, 0.0, 0.0), (0.58403360843658447, 0.0, 0.0), (0.58823531866073608, 0.0, 0.0), (0.59243696928024292, 0.0, 0.0), (0.59663867950439453, 0.0, 0.0), (0.60084033012390137, 0.0, 0.0), (0.60504204034805298, 0.0, 0.0), (0.60924369096755981, 0.0, 0.0), (0.61344540119171143, 0.0, 0.0), (0.61764705181121826, 0.0, 0.0), (0.62184876203536987, 0.0, 0.0), (0.62605041265487671, 0.0, 0.0), (0.63025212287902832, 0.0, 0.0), (0.63445377349853516, 0.0, 0.0), (0.63865548372268677, 0.0, 0.0), (0.6428571343421936, 0.0, 0.0), (0.64705884456634521, 0.0, 0.0), (0.65126049518585205, 0.0, 0.0), (0.65546220541000366, 0.0, 0.0), (0.6596638560295105, 0.0, 0.0), (0.66386556625366211, 0.0, 0.0), (0.66806721687316895, 0.0, 0.0), (0.67226892709732056, 0.0, 0.0), (0.67647057771682739, 0.0, 0.0), (0.680672287940979, 0.0, 0.0), (0.68487393856048584, 0.0, 0.0), (0.68907564878463745, 0.0, 0.0), (0.69327729940414429, 0.0, 0.0), (0.6974790096282959, 0.0, 0.0), (0.70168066024780273, 0.0, 0.0), (0.70588237047195435, 0.0, 0.0), (0.71008402109146118, 0.0, 0.0), (0.71428573131561279, 0.0, 0.0), (0.71848738193511963, 0.0, 0.0), (0.72268909215927124, 0.0, 0.0), (0.72689074277877808, 0.0, 0.0), (0.73109245300292969, 0.0, 0.0), (0.73529410362243652, 0.0, 0.0), (0.73949581384658813, 0.0, 0.0), (0.74369746446609497, 0.0, 0.0), (0.74789917469024658, 0.0, 0.0), (0.75210082530975342, 0.0, 0.0), (0.75630253553390503, 0.0, 0.0), (0.76050418615341187, 0.0, 0.0), (0.76470589637756348, 0.0, 0.0), (0.76890754699707031, 0.0, 0.0), (0.77310925722122192, 0.0, 0.0), (0.77731090784072876, 0.0, 0.0), (0.78151261806488037, 0.0078431377187371254, 0.0078431377187371254), (0.78571426868438721, 0.027450980618596077, 0.027450980618596077), (0.78991597890853882, 0.070588238537311554, 0.070588238537311554), (0.79411762952804565, 0.094117648899555206, 0.094117648899555206), (0.79831933975219727, 0.11372549086809158, 0.11372549086809158), (0.8025209903717041, 0.13333334028720856, 0.13333334028720856), (0.80672270059585571, 0.15686275064945221, 0.15686275064945221), (0.81092435121536255, 0.17647059261798859, 0.17647059261798859), (0.81512606143951416, 0.19607843458652496, 0.19607843458652496), (0.819327712059021, 0.21960784494876862, 0.21960784494876862), (0.82352942228317261, 0.23921568691730499, 0.23921568691730499), (0.82773107290267944, 0.26274511218070984, 0.26274511218070984), (0.83193278312683105, 0.28235295414924622, 0.28235295414924622), (0.83613443374633789, 0.30196079611778259, 0.30196079611778259), (0.8403361439704895, 0.32549020648002625, 0.32549020648002625), (0.84453779458999634, 0.34509804844856262, 0.34509804844856262), (0.84873950481414795, 0.364705890417099, 0.364705890417099), (0.85294115543365479, 0.40784314274787903, 0.40784314274787903), (0.8571428656578064, 0.43137255311012268, 0.43137255311012268), (0.86134451627731323, 0.45098039507865906, 0.45098039507865906), (0.86554622650146484, 0.47058823704719543, 0.47058823704719543), (0.86974787712097168, 0.49411764740943909, 0.49411764740943909), (0.87394958734512329, 0.51372551918029785, 0.51372551918029785), (0.87815123796463013, 0.53333336114883423, 0.53333336114883423), (0.88235294818878174, 0.55686277151107788, 0.55686277151107788), (0.88655459880828857, 0.57647061347961426, 0.57647061347961426), (0.89075630903244019, 0.60000002384185791, 0.60000002384185791), (0.89495795965194702, 0.61960786581039429, 0.61960786581039429), (0.89915966987609863, 0.63921570777893066, 0.63921570777893066), (0.90336132049560547, 0.66274511814117432, 0.66274511814117432), (0.90756303071975708, 0.68235296010971069, 0.68235296010971069), (0.91176468133926392, 0.70588237047195435, 0.70588237047195435), (0.91596639156341553, 0.7450980544090271, 0.7450980544090271), (0.92016804218292236, 0.76862746477127075, 0.76862746477127075), (0.92436975240707397, 0.78823530673980713, 0.78823530673980713), (0.92857140302658081, 0.80784314870834351, 0.80784314870834351), (0.93277311325073242, 0.83137255907058716, 0.83137255907058716), (0.93697476387023926, 0.85098040103912354, 0.85098040103912354), (0.94117647409439087, 0.87450981140136719, 0.87450981140136719), (0.94537812471389771, 0.89411765336990356, 0.89411765336990356), (0.94957983493804932, 0.91372549533843994, 0.91372549533843994), (0.95378148555755615, 0.93725490570068359, 0.93725490570068359), (0.95798319578170776, 0.95686274766921997, 0.95686274766921997), (0.9621848464012146, 0.97647058963775635, 0.97647058963775635), (0.96638655662536621, 1.0, 1.0), (0.97058820724487305, 1.0, 1.0), (0.97478991746902466, 1.0, 1.0), (0.97899156808853149, 1.0, 1.0), (0.98319327831268311, 1.0, 1.0), (0.98739492893218994, 1.0, 1.0), (0.99159663915634155, 1.0, 1.0), (0.99579828977584839, 1.0, 1.0), (1.0, 1.0, 1.0)], green = [(0.0, 0.0, 0.0), (0.0042016808874905109, 0.0, 0.0), (0.0084033617749810219, 0.0, 0.0), (0.012605042196810246, 0.0, 0.0), (0.016806723549962044, 0.0, 0.0), (0.021008403971791267, 0.0, 0.0), (0.025210084393620491, 0.0, 0.0), (0.029411764815449715, 0.0, 0.0), (0.033613447099924088, 0.019607843831181526, 0.019607843831181526), (0.037815127521753311, 0.043137256056070328, 0.043137256056070328), (0.042016807943582535, 0.062745101749897003, 0.062745101749897003), (0.046218488365411758, 0.086274512112140656, 0.086274512112140656), (0.050420168787240982, 0.10588235408067703, 0.10588235408067703), (0.054621849209070206, 0.12549020349979401, 0.12549020349979401), (0.058823529630899429, 0.14901961386203766, 0.14901961386203766), (0.063025213778018951, 0.16862745583057404, 0.16862745583057404), (0.067226894199848175, 0.18823529779911041, 0.18823529779911041), (0.071428574621677399, 0.21176470816135406, 0.21176470816135406), (0.075630255043506622, 0.23137255012989044, 0.23137255012989044), (0.079831935465335846, 0.25490197539329529, 0.25490197539329529), (0.08403361588716507, 0.27450981736183167, 0.27450981736183167), (0.088235296308994293, 0.29411765933036804, 0.29411765933036804), (0.092436976730823517, 0.31764706969261169, 0.31764706969261169), (0.09663865715265274, 0.35686275362968445, 0.35686275362968445), (0.10084033757448196, 0.3803921639919281, 0.3803921639919281), (0.10504201799631119, 0.40000000596046448, 0.40000000596046448), (0.10924369841814041, 0.42352941632270813, 0.42352941632270813), (0.11344537883996964, 0.44313725829124451, 0.44313725829124451), (0.11764705926179886, 0.46274510025978088, 0.46274510025978088), (0.12184873968362808, 0.48627451062202454, 0.48627451062202454), (0.1260504275560379, 0.5058823823928833, 0.5058823823928833), (0.13025210797786713, 0.52941179275512695, 0.52941179275512695), (0.13445378839969635, 0.54901963472366333, 0.54901963472366333), (0.13865546882152557, 0.56862747669219971, 0.56862747669219971), (0.1428571492433548, 0.59215688705444336, 0.59215688705444336), (0.14705882966518402, 0.61176472902297974, 0.61176472902297974), (0.15126051008701324, 0.63137257099151611, 0.63137257099151611), (0.15546219050884247, 0.65490198135375977, 0.65490198135375977), (0.15966387093067169, 0.69803923368453979, 0.69803923368453979), (0.16386555135250092, 0.71764707565307617, 0.71764707565307617), (0.16806723177433014, 0.73725491762161255, 0.73725491762161255), (0.17226891219615936, 0.7607843279838562, 0.7607843279838562), (0.17647059261798859, 0.78039216995239258, 0.78039216995239258), (0.18067227303981781, 0.80000001192092896, 0.80000001192092896), (0.18487395346164703, 0.82352942228317261, 0.82352942228317261), (0.18907563388347626, 0.84313726425170898, 0.84313726425170898), (0.19327731430530548, 0.86666667461395264, 0.86666667461395264), (0.1974789947271347, 0.88627451658248901, 0.88627451658248901), (0.20168067514896393, 0.90588235855102539, 0.90588235855102539), (0.20588235557079315, 0.92941176891326904, 0.92941176891326904), (0.21008403599262238, 0.94901961088180542, 0.94901961088180542), (0.2142857164144516, 0.9686274528503418, 0.9686274528503418), (0.21848739683628082, 0.99215686321258545, 0.99215686321258545), (0.22268907725811005, 1.0, 1.0), (0.22689075767993927, 1.0, 1.0), (0.23109243810176849, 1.0, 1.0), (0.23529411852359772, 1.0, 1.0), (0.23949579894542694, 1.0, 1.0), (0.24369747936725616, 1.0, 1.0), (0.24789915978908539, 1.0, 1.0), (0.25210085511207581, 1.0, 1.0), (0.25630253553390503, 1.0, 1.0), (0.26050421595573425, 1.0, 1.0), (0.26470589637756348, 1.0, 1.0), (0.2689075767993927, 1.0, 1.0), (0.27310925722122192, 1.0, 1.0), (0.27731093764305115, 1.0, 1.0), (0.28151261806488037, 1.0, 1.0), (0.28571429848670959, 1.0, 1.0), (0.28991597890853882, 1.0, 1.0), (0.29411765933036804, 1.0, 1.0), (0.29831933975219727, 1.0, 1.0), (0.30252102017402649, 1.0, 1.0), (0.30672270059585571, 1.0, 1.0), (0.31092438101768494, 1.0, 1.0), (0.31512606143951416, 1.0, 1.0), (0.31932774186134338, 1.0, 1.0), (0.32352942228317261, 1.0, 1.0), (0.32773110270500183, 1.0, 1.0), (0.33193278312683105, 1.0, 1.0), (0.33613446354866028, 1.0, 1.0), (0.3403361439704895, 1.0, 1.0), (0.34453782439231873, 1.0, 1.0), (0.34873950481414795, 1.0, 1.0), (0.35294118523597717, 1.0, 1.0), (0.3571428656578064, 1.0, 1.0), (0.36134454607963562, 1.0, 1.0), (0.36554622650146484, 1.0, 1.0), (0.36974790692329407, 1.0, 1.0), (0.37394958734512329, 1.0, 1.0), (0.37815126776695251, 1.0, 1.0), (0.38235294818878174, 1.0, 1.0), (0.38655462861061096, 1.0, 1.0), (0.39075630903244019, 1.0, 1.0), (0.39495798945426941, 1.0, 1.0), (0.39915966987609863, 1.0, 1.0), (0.40336135029792786, 1.0, 1.0), (0.40756303071975708, 1.0, 1.0), (0.4117647111415863, 1.0, 1.0), (0.41596639156341553, 1.0, 1.0), (0.42016807198524475, 1.0, 1.0), (0.42436975240707397, 1.0, 1.0), (0.4285714328289032, 1.0, 1.0), (0.43277311325073242, 1.0, 1.0), (0.43697479367256165, 1.0, 1.0), (0.44117647409439087, 1.0, 1.0), (0.44537815451622009, 1.0, 1.0), (0.44957983493804932, 1.0, 1.0), (0.45378151535987854, 1.0, 1.0), (0.45798319578170776, 1.0, 1.0), (0.46218487620353699, 1.0, 1.0), (0.46638655662536621, 1.0, 1.0), (0.47058823704719543, 1.0, 1.0), (0.47478991746902466, 1.0, 1.0), (0.47899159789085388, 1.0, 1.0), (0.48319327831268311, 1.0, 1.0), (0.48739495873451233, 1.0, 1.0), (0.49159663915634155, 1.0, 1.0), (0.49579831957817078, 1.0, 1.0), (0.5, 1.0, 1.0), (0.50420171022415161, 1.0, 1.0), (0.50840336084365845, 1.0, 1.0), (0.51260507106781006, 1.0, 1.0), (0.51680672168731689, 1.0, 1.0), (0.52100843191146851, 1.0, 1.0), (0.52521008253097534, 1.0, 1.0), (0.52941179275512695, 1.0, 1.0), (0.53361344337463379, 1.0, 1.0), (0.5378151535987854, 1.0, 1.0), (0.54201680421829224, 1.0, 1.0), (0.54621851444244385, 1.0, 1.0), (0.55042016506195068, 1.0, 1.0), (0.55462187528610229, 1.0, 1.0), (0.55882352590560913, 1.0, 1.0), (0.56302523612976074, 1.0, 1.0), (0.56722688674926758, 1.0, 1.0), (0.57142859697341919, 1.0, 1.0), (0.57563024759292603, 1.0, 1.0), (0.57983195781707764, 1.0, 1.0), (0.58403360843658447, 1.0, 1.0), (0.58823531866073608, 1.0, 1.0), (0.59243696928024292, 1.0, 1.0), (0.59663867950439453, 0.98039215803146362, 0.98039215803146362), (0.60084033012390137, 0.93725490570068359, 0.93725490570068359), (0.60504204034805298, 0.91764706373214722, 0.91764706373214722), (0.60924369096755981, 0.89411765336990356, 0.89411765336990356), (0.61344540119171143, 0.87450981140136719, 0.87450981140136719), (0.61764705181121826, 0.85490196943283081, 0.85490196943283081), (0.62184876203536987, 0.83137255907058716, 0.83137255907058716), (0.62605041265487671, 0.81176471710205078, 0.81176471710205078), (0.63025212287902832, 0.78823530673980713, 0.78823530673980713), (0.63445377349853516, 0.76862746477127075, 0.76862746477127075), (0.63865548372268677, 0.74901962280273438, 0.74901962280273438), (0.6428571343421936, 0.72549021244049072, 0.72549021244049072), (0.64705884456634521, 0.70588237047195435, 0.70588237047195435), (0.65126049518585205, 0.68235296010971069, 0.68235296010971069), (0.65546220541000366, 0.66274511814117432, 0.66274511814117432), (0.6596638560295105, 0.64313727617263794, 0.64313727617263794), (0.66386556625366211, 0.60000002384185791, 0.60000002384185791), (0.66806721687316895, 0.58039218187332153, 0.58039218187332153), (0.67226892709732056, 0.55686277151107788, 0.55686277151107788), (0.67647057771682739, 0.5372549295425415, 0.5372549295425415), (0.680672287940979, 0.51372551918029785, 0.51372551918029785), (0.68487393856048584, 0.49411764740943909, 0.49411764740943909), (0.68907564878463745, 0.47450980544090271, 0.47450980544090271), (0.69327729940414429, 0.45098039507865906, 0.45098039507865906), (0.6974790096282959, 0.43137255311012268, 0.43137255311012268), (0.70168066024780273, 0.4117647111415863, 0.4117647111415863), (0.70588237047195435, 0.38823530077934265, 0.38823530077934265), (0.71008402109146118, 0.36862745881080627, 0.36862745881080627), (0.71428573131561279, 0.34509804844856262, 0.34509804844856262), (0.71848738193511963, 0.32549020648002625, 0.32549020648002625), (0.72268909215927124, 0.30588236451148987, 0.30588236451148987), (0.72689074277877808, 0.26274511218070984, 0.26274511218070984), (0.73109245300292969, 0.24313725531101227, 0.24313725531101227), (0.73529410362243652, 0.21960784494876862, 0.21960784494876862), (0.73949581384658813, 0.20000000298023224, 0.20000000298023224), (0.74369746446609497, 0.17647059261798859, 0.17647059261798859), (0.74789917469024658, 0.15686275064945221, 0.15686275064945221), (0.75210082530975342, 0.13725490868091583, 0.13725490868091583), (0.75630253553390503, 0.11372549086809158, 0.11372549086809158), (0.76050418615341187, 0.094117648899555206, 0.094117648899555206), (0.76470589637756348, 0.070588238537311554, 0.070588238537311554), (0.76890754699707031, 0.050980392843484879, 0.050980392843484879), (0.77310925722122192, 0.031372550874948502, 0.031372550874948502), (0.77731090784072876, 0.0078431377187371254, 0.0078431377187371254), (0.78151261806488037, 0.0, 0.0), (0.78571426868438721, 0.0, 0.0), (0.78991597890853882, 0.0, 0.0), (0.79411762952804565, 0.0, 0.0), (0.79831933975219727, 0.0, 0.0), (0.8025209903717041, 0.0, 0.0), (0.80672270059585571, 0.0, 0.0), (0.81092435121536255, 0.0, 0.0), (0.81512606143951416, 0.0, 0.0), (0.819327712059021, 0.0, 0.0), (0.82352942228317261, 0.0, 0.0), (0.82773107290267944, 0.0, 0.0), (0.83193278312683105, 0.0, 0.0), (0.83613443374633789, 0.0, 0.0), (0.8403361439704895, 0.0, 0.0), (0.84453779458999634, 0.0, 0.0), (0.84873950481414795, 0.0, 0.0), (0.85294115543365479, 0.0, 0.0), (0.8571428656578064, 0.0, 0.0), (0.86134451627731323, 0.0, 0.0), (0.86554622650146484, 0.0, 0.0), (0.86974787712097168, 0.0, 0.0), (0.87394958734512329, 0.0, 0.0), (0.87815123796463013, 0.0, 0.0), (0.88235294818878174, 0.0, 0.0), (0.88655459880828857, 0.0, 0.0), (0.89075630903244019, 0.0, 0.0), (0.89495795965194702, 0.0, 0.0), (0.89915966987609863, 0.0, 0.0), (0.90336132049560547, 0.0, 0.0), (0.90756303071975708, 0.0, 0.0), (0.91176468133926392, 0.0, 0.0), (0.91596639156341553, 0.0, 0.0), (0.92016804218292236, 0.0, 0.0), (0.92436975240707397, 0.0, 0.0), (0.92857140302658081, 0.0, 0.0), (0.93277311325073242, 0.0, 0.0), (0.93697476387023926, 0.0, 0.0), (0.94117647409439087, 0.0, 0.0), (0.94537812471389771, 0.0, 0.0), (0.94957983493804932, 0.0, 0.0), (0.95378148555755615, 0.0, 0.0), (0.95798319578170776, 0.0, 0.0), (0.9621848464012146, 0.0, 0.0), (0.96638655662536621, 0.0, 0.0), (0.97058820724487305, 0.0, 0.0), (0.97478991746902466, 0.0, 0.0), (0.97899156808853149, 0.0, 0.0), (0.98319327831268311, 0.0, 0.0), (0.98739492893218994, 0.0, 0.0), (0.99159663915634155, 0.0, 0.0), (0.99579828977584839, 0.0, 0.0), (1.0, 0.0, 0.0)], blue = [(0.0, 0.16470588743686676, 0.16470588743686676), (0.0042016808874905109, 0.14117647707462311, 0.14117647707462311), (0.0084033617749810219, 0.12156862765550613, 0.12156862765550613), (0.012605042196810246, 0.10196078568696976, 0.10196078568696976), (0.016806723549962044, 0.078431375324726105, 0.078431375324726105), (0.021008403971791267, 0.058823529630899429, 0.058823529630899429), (0.025210084393620491, 0.039215687662363052, 0.039215687662363052), (0.029411764815449715, 0.015686275437474251, 0.015686275437474251), (0.033613447099924088, 0.0, 0.0), (0.037815127521753311, 0.0, 0.0), (0.042016807943582535, 0.0, 0.0), (0.046218488365411758, 0.0, 0.0), (0.050420168787240982, 0.0, 0.0), (0.054621849209070206, 0.0, 0.0), (0.058823529630899429, 0.0, 0.0), (0.063025213778018951, 0.0, 0.0), (0.067226894199848175, 0.0, 0.0), (0.071428574621677399, 0.0, 0.0), (0.075630255043506622, 0.0, 0.0), (0.079831935465335846, 0.0, 0.0), (0.08403361588716507, 0.0, 0.0), (0.088235296308994293, 0.0, 0.0), (0.092436976730823517, 0.0, 0.0), (0.09663865715265274, 0.0, 0.0), (0.10084033757448196, 0.0, 0.0), (0.10504201799631119, 0.0, 0.0), (0.10924369841814041, 0.0, 0.0), (0.11344537883996964, 0.0, 0.0), (0.11764705926179886, 0.0, 0.0), (0.12184873968362808, 0.0, 0.0), (0.1260504275560379, 0.0, 0.0), (0.13025210797786713, 0.0, 0.0), (0.13445378839969635, 0.0, 0.0), (0.13865546882152557, 0.0, 0.0), (0.1428571492433548, 0.0, 0.0), (0.14705882966518402, 0.0, 0.0), (0.15126051008701324, 0.0, 0.0), (0.15546219050884247, 0.0, 0.0), (0.15966387093067169, 0.0, 0.0), (0.16386555135250092, 0.0, 0.0), (0.16806723177433014, 0.0, 0.0), (0.17226891219615936, 0.0, 0.0), (0.17647059261798859, 0.0, 0.0), (0.18067227303981781, 0.0, 0.0), (0.18487395346164703, 0.0, 0.0), (0.18907563388347626, 0.0, 0.0), (0.19327731430530548, 0.0, 0.0), (0.1974789947271347, 0.0, 0.0), (0.20168067514896393, 0.0, 0.0), (0.20588235557079315, 0.0, 0.0), (0.21008403599262238, 0.0, 0.0), (0.2142857164144516, 0.0, 0.0), (0.21848739683628082, 0.0, 0.0), (0.22268907725811005, 0.0, 0.0), (0.22689075767993927, 0.0, 0.0), (0.23109243810176849, 0.0, 0.0), (0.23529411852359772, 0.0, 0.0), (0.23949579894542694, 0.0, 0.0), (0.24369747936725616, 0.0, 0.0), (0.24789915978908539, 0.0, 0.0), (0.25210085511207581, 0.0, 0.0), (0.25630253553390503, 0.0, 0.0), (0.26050421595573425, 0.0, 0.0), (0.26470589637756348, 0.0, 0.0), (0.2689075767993927, 0.0, 0.0), (0.27310925722122192, 0.0, 0.0), (0.27731093764305115, 0.0, 0.0), (0.28151261806488037, 0.0, 0.0), (0.28571429848670959, 0.0, 0.0), (0.28991597890853882, 0.0, 0.0), (0.29411765933036804, 0.0, 0.0), (0.29831933975219727, 0.0, 0.0), (0.30252102017402649, 0.0, 0.0), (0.30672270059585571, 0.0, 0.0), (0.31092438101768494, 0.0, 0.0), (0.31512606143951416, 0.0, 0.0), (0.31932774186134338, 0.0, 0.0), (0.32352942228317261, 0.0, 0.0), (0.32773110270500183, 0.0, 0.0), (0.33193278312683105, 0.0, 0.0), (0.33613446354866028, 0.0, 0.0), (0.3403361439704895, 0.0, 0.0), (0.34453782439231873, 0.0, 0.0), (0.34873950481414795, 0.0, 0.0), (0.35294118523597717, 0.0, 0.0), (0.3571428656578064, 0.0, 0.0), (0.36134454607963562, 0.0, 0.0), (0.36554622650146484, 0.0, 0.0), (0.36974790692329407, 0.0, 0.0), (0.37394958734512329, 0.0, 0.0), (0.37815126776695251, 0.0, 0.0), (0.38235294818878174, 0.0, 0.0), (0.38655462861061096, 0.0, 0.0), (0.39075630903244019, 0.0, 0.0), (0.39495798945426941, 0.0, 0.0), (0.39915966987609863, 0.0, 0.0), (0.40336135029792786, 0.0, 0.0), (0.40756303071975708, 0.0039215688593685627, 0.0039215688593685627), (0.4117647111415863, 0.047058824449777603, 0.047058824449777603), (0.41596639156341553, 0.066666670143604279, 0.066666670143604279), (0.42016807198524475, 0.090196080505847931, 0.090196080505847931), (0.42436975240707397, 0.10980392247438431, 0.10980392247438431), (0.4285714328289032, 0.12941177189350128, 0.12941177189350128), (0.43277311325073242, 0.15294118225574493, 0.15294118225574493), (0.43697479367256165, 0.17254902422428131, 0.17254902422428131), (0.44117647409439087, 0.19215686619281769, 0.19215686619281769), (0.44537815451622009, 0.21568627655506134, 0.21568627655506134), (0.44957983493804932, 0.23529411852359772, 0.23529411852359772), (0.45378151535987854, 0.25882354378700256, 0.25882354378700256), (0.45798319578170776, 0.27843138575553894, 0.27843138575553894), (0.46218487620353699, 0.29803922772407532, 0.29803922772407532), (0.46638655662536621, 0.32156863808631897, 0.32156863808631897), (0.47058823704719543, 0.34117648005485535, 0.34117648005485535), (0.47478991746902466, 0.38431373238563538, 0.38431373238563538), (0.47899159789085388, 0.40392157435417175, 0.40392157435417175), (0.48319327831268311, 0.42745098471641541, 0.42745098471641541), (0.48739495873451233, 0.44705882668495178, 0.44705882668495178), (0.49159663915634155, 0.46666666865348816, 0.46666666865348816), (0.49579831957817078, 0.49019607901573181, 0.49019607901573181), (0.5, 0.50980395078659058, 0.50980395078659058), (0.50420171022415161, 0.52941179275512695, 0.52941179275512695), (0.50840336084365845, 0.55294120311737061, 0.55294120311737061), (0.51260507106781006, 0.57254904508590698, 0.57254904508590698), (0.51680672168731689, 0.59607845544815063, 0.59607845544815063), (0.52100843191146851, 0.61568629741668701, 0.61568629741668701), (0.52521008253097534, 0.63529413938522339, 0.63529413938522339), (0.52941179275512695, 0.65882354974746704, 0.65882354974746704), (0.53361344337463379, 0.67843139171600342, 0.67843139171600342), (0.5378151535987854, 0.72156864404678345, 0.72156864404678345), (0.54201680421829224, 0.74117648601531982, 0.74117648601531982), (0.54621851444244385, 0.76470589637756348, 0.76470589637756348), (0.55042016506195068, 0.78431373834609985, 0.78431373834609985), (0.55462187528610229, 0.80392158031463623, 0.80392158031463623), (0.55882352590560913, 0.82745099067687988, 0.82745099067687988), (0.56302523612976074, 0.84705883264541626, 0.84705883264541626), (0.56722688674926758, 0.87058824300765991, 0.87058824300765991), (0.57142859697341919, 0.89019608497619629, 0.89019608497619629), (0.57563024759292603, 0.90980392694473267, 0.90980392694473267), (0.57983195781707764, 0.93333333730697632, 0.93333333730697632), (0.58403360843658447, 0.9529411792755127, 0.9529411792755127), (0.58823531866073608, 0.97254902124404907, 0.97254902124404907), (0.59243696928024292, 0.99607843160629272, 0.99607843160629272), (0.59663867950439453, 1.0, 1.0), (0.60084033012390137, 1.0, 1.0), (0.60504204034805298, 1.0, 1.0), (0.60924369096755981, 1.0, 1.0), (0.61344540119171143, 1.0, 1.0), (0.61764705181121826, 1.0, 1.0), (0.62184876203536987, 1.0, 1.0), (0.62605041265487671, 1.0, 1.0), (0.63025212287902832, 1.0, 1.0), (0.63445377349853516, 1.0, 1.0), (0.63865548372268677, 1.0, 1.0), (0.6428571343421936, 1.0, 1.0), (0.64705884456634521, 1.0, 1.0), (0.65126049518585205, 1.0, 1.0), (0.65546220541000366, 1.0, 1.0), (0.6596638560295105, 1.0, 1.0), (0.66386556625366211, 1.0, 1.0), (0.66806721687316895, 1.0, 1.0), (0.67226892709732056, 1.0, 1.0), (0.67647057771682739, 1.0, 1.0), (0.680672287940979, 1.0, 1.0), (0.68487393856048584, 1.0, 1.0), (0.68907564878463745, 1.0, 1.0), (0.69327729940414429, 1.0, 1.0), (0.6974790096282959, 1.0, 1.0), (0.70168066024780273, 1.0, 1.0), (0.70588237047195435, 1.0, 1.0), (0.71008402109146118, 1.0, 1.0), (0.71428573131561279, 1.0, 1.0), (0.71848738193511963, 1.0, 1.0), (0.72268909215927124, 1.0, 1.0), (0.72689074277877808, 1.0, 1.0), (0.73109245300292969, 1.0, 1.0), (0.73529410362243652, 1.0, 1.0), (0.73949581384658813, 1.0, 1.0), (0.74369746446609497, 1.0, 1.0), (0.74789917469024658, 1.0, 1.0), (0.75210082530975342, 1.0, 1.0), (0.75630253553390503, 1.0, 1.0), (0.76050418615341187, 1.0, 1.0), (0.76470589637756348, 1.0, 1.0), (0.76890754699707031, 1.0, 1.0), (0.77310925722122192, 1.0, 1.0), (0.77731090784072876, 1.0, 1.0), (0.78151261806488037, 1.0, 1.0), (0.78571426868438721, 1.0, 1.0), (0.78991597890853882, 1.0, 1.0), (0.79411762952804565, 1.0, 1.0), (0.79831933975219727, 1.0, 1.0), (0.8025209903717041, 1.0, 1.0), (0.80672270059585571, 1.0, 1.0), (0.81092435121536255, 1.0, 1.0), (0.81512606143951416, 1.0, 1.0), (0.819327712059021, 1.0, 1.0), (0.82352942228317261, 1.0, 1.0), (0.82773107290267944, 1.0, 1.0), (0.83193278312683105, 1.0, 1.0), (0.83613443374633789, 1.0, 1.0), (0.8403361439704895, 1.0, 1.0), (0.84453779458999634, 1.0, 1.0), (0.84873950481414795, 1.0, 1.0), (0.85294115543365479, 1.0, 1.0), (0.8571428656578064, 1.0, 1.0), (0.86134451627731323, 1.0, 1.0), (0.86554622650146484, 1.0, 1.0), (0.86974787712097168, 1.0, 1.0), (0.87394958734512329, 1.0, 1.0), (0.87815123796463013, 1.0, 1.0), (0.88235294818878174, 1.0, 1.0), (0.88655459880828857, 1.0, 1.0), (0.89075630903244019, 1.0, 1.0), (0.89495795965194702, 1.0, 1.0), (0.89915966987609863, 1.0, 1.0), (0.90336132049560547, 1.0, 1.0), (0.90756303071975708, 1.0, 1.0), (0.91176468133926392, 1.0, 1.0), (0.91596639156341553, 1.0, 1.0), (0.92016804218292236, 1.0, 1.0), (0.92436975240707397, 1.0, 1.0), (0.92857140302658081, 1.0, 1.0), (0.93277311325073242, 1.0, 1.0), (0.93697476387023926, 1.0, 1.0), (0.94117647409439087, 1.0, 1.0), (0.94537812471389771, 1.0, 1.0), (0.94957983493804932, 1.0, 1.0), (0.95378148555755615, 1.0, 1.0), (0.95798319578170776, 1.0, 1.0), (0.9621848464012146, 1.0, 1.0), (0.96638655662536621, 0.99607843160629272, 0.99607843160629272), (0.97058820724487305, 0.97647058963775635, 0.97647058963775635), (0.97478991746902466, 0.9529411792755127, 0.9529411792755127), (0.97899156808853149, 0.91372549533843994, 0.91372549533843994), (0.98319327831268311, 0.89019608497619629, 0.89019608497619629), (0.98739492893218994, 0.87058824300765991, 0.87058824300765991), (0.99159663915634155, 0.85098040103912354, 0.85098040103912354), (0.99579828977584839, 0.82745099067687988, 0.82745099067687988), (1.0, 0.80784314870834351, 0.80784314870834351)], ) return ColorMapper.from_segment_map(_data, range=range, **traits) def gist_stern(range, **traits): """ Generator for the 'gist_stern' colormap from GIST. """ _data = dict( red = [(0.0, 0.0, 0.0), (0.0042016808874905109, 0.070588238537311554, 0.070588238537311554), (0.0084033617749810219, 0.14117647707462311, 0.14117647707462311), (0.012605042196810246, 0.21176470816135406, 0.21176470816135406), (0.016806723549962044, 0.28235295414924622, 0.28235295414924622), (0.021008403971791267, 0.35294118523597717, 0.35294118523597717), (0.025210084393620491, 0.42352941632270813, 0.42352941632270813), (0.029411764815449715, 0.49803921580314636, 0.49803921580314636), (0.033613447099924088, 0.56862747669219971, 0.56862747669219971), (0.037815127521753311, 0.63921570777893066, 0.63921570777893066), (0.042016807943582535, 0.78039216995239258, 0.78039216995239258), (0.046218488365411758, 0.85098040103912354, 0.85098040103912354), (0.050420168787240982, 0.92156863212585449, 0.92156863212585449), (0.054621849209070206, 0.99607843160629272, 0.99607843160629272), (0.058823529630899429, 0.97647058963775635, 0.97647058963775635), (0.063025213778018951, 0.95686274766921997, 0.95686274766921997), (0.067226894199848175, 0.93725490570068359, 0.93725490570068359), (0.071428574621677399, 0.91764706373214722, 0.91764706373214722), (0.075630255043506622, 0.89803922176361084, 0.89803922176361084), (0.079831935465335846, 0.87450981140136719, 0.87450981140136719), (0.08403361588716507, 0.85490196943283081, 0.85490196943283081), (0.088235296308994293, 0.83529412746429443, 0.83529412746429443), (0.092436976730823517, 0.81568628549575806, 0.81568628549575806), (0.09663865715265274, 0.79607844352722168, 0.79607844352722168), (0.10084033757448196, 0.77254903316497803, 0.77254903316497803), (0.10504201799631119, 0.75294119119644165, 0.75294119119644165), (0.10924369841814041, 0.73333334922790527, 0.73333334922790527), (0.11344537883996964, 0.7137255072593689, 0.7137255072593689), (0.11764705926179886, 0.69411766529083252, 0.69411766529083252), (0.12184873968362808, 0.67450982332229614, 0.67450982332229614), (0.1260504275560379, 0.63137257099151611, 0.63137257099151611), (0.13025210797786713, 0.61176472902297974, 0.61176472902297974), (0.13445378839969635, 0.59215688705444336, 0.59215688705444336), (0.13865546882152557, 0.57254904508590698, 0.57254904508590698), (0.1428571492433548, 0.54901963472366333, 0.54901963472366333), (0.14705882966518402, 0.52941179275512695, 0.52941179275512695), (0.15126051008701324, 0.50980395078659058, 0.50980395078659058), (0.15546219050884247, 0.49019607901573181, 0.49019607901573181), (0.15966387093067169, 0.47058823704719543, 0.47058823704719543), (0.16386555135250092, 0.45098039507865906, 0.45098039507865906), (0.16806723177433014, 0.42745098471641541, 0.42745098471641541), (0.17226891219615936, 0.40784314274787903, 0.40784314274787903), (0.17647059261798859, 0.38823530077934265, 0.38823530077934265), (0.18067227303981781, 0.36862745881080627, 0.36862745881080627), (0.18487395346164703, 0.3490196168422699, 0.3490196168422699), (0.18907563388347626, 0.32549020648002625, 0.32549020648002625), (0.19327731430530548, 0.30588236451148987, 0.30588236451148987), (0.1974789947271347, 0.28627452254295349, 0.28627452254295349), (0.20168067514896393, 0.26666668057441711, 0.26666668057441711), (0.20588235557079315, 0.24705882370471954, 0.24705882370471954), (0.21008403599262238, 0.20392157137393951, 0.20392157137393951), (0.2142857164144516, 0.18431372940540314, 0.18431372940540314), (0.21848739683628082, 0.16470588743686676, 0.16470588743686676), (0.22268907725811005, 0.14509804546833038, 0.14509804546833038), (0.22689075767993927, 0.12549020349979401, 0.12549020349979401), (0.23109243810176849, 0.10196078568696976, 0.10196078568696976), (0.23529411852359772, 0.08235294371843338, 0.08235294371843338), (0.23949579894542694, 0.062745101749897003, 0.062745101749897003), (0.24369747936725616, 0.043137256056070328, 0.043137256056070328), (0.24789915978908539, 0.023529412224888802, 0.023529412224888802), (0.25210085511207581, 0.25098040699958801, 0.25098040699958801), (0.25630253553390503, 0.25490197539329529, 0.25490197539329529), (0.26050421595573425, 0.25882354378700256, 0.25882354378700256), (0.26470589637756348, 0.26274511218070984, 0.26274511218070984), (0.2689075767993927, 0.26666668057441711, 0.26666668057441711), (0.27310925722122192, 0.27058824896812439, 0.27058824896812439), (0.27731093764305115, 0.27450981736183167, 0.27450981736183167), (0.28151261806488037, 0.27843138575553894, 0.27843138575553894), (0.28571429848670959, 0.28235295414924622, 0.28235295414924622), (0.28991597890853882, 0.28627452254295349, 0.28627452254295349), (0.29411765933036804, 0.29411765933036804, 0.29411765933036804), (0.29831933975219727, 0.29803922772407532, 0.29803922772407532), (0.30252102017402649, 0.30196079611778259, 0.30196079611778259), (0.30672270059585571, 0.30588236451148987, 0.30588236451148987), (0.31092438101768494, 0.30980393290519714, 0.30980393290519714), (0.31512606143951416, 0.31372550129890442, 0.31372550129890442), (0.31932774186134338, 0.31764706969261169, 0.31764706969261169), (0.32352942228317261, 0.32156863808631897, 0.32156863808631897), (0.32773110270500183, 0.32549020648002625, 0.32549020648002625), (0.33193278312683105, 0.32941177487373352, 0.32941177487373352), (0.33613446354866028, 0.3333333432674408, 0.3333333432674408), (0.3403361439704895, 0.33725491166114807, 0.33725491166114807), (0.34453782439231873, 0.34117648005485535, 0.34117648005485535), (0.34873950481414795, 0.34509804844856262, 0.34509804844856262), (0.35294118523597717, 0.3490196168422699, 0.3490196168422699), (0.3571428656578064, 0.35294118523597717, 0.35294118523597717), (0.36134454607963562, 0.35686275362968445, 0.35686275362968445), (0.36554622650146484, 0.36078432202339172, 0.36078432202339172), (0.36974790692329407, 0.364705890417099, 0.364705890417099), (0.37394958734512329, 0.36862745881080627, 0.36862745881080627), (0.37815126776695251, 0.37647059559822083, 0.37647059559822083), (0.38235294818878174, 0.3803921639919281, 0.3803921639919281), (0.38655462861061096, 0.38431373238563538, 0.38431373238563538), (0.39075630903244019, 0.38823530077934265, 0.38823530077934265), (0.39495798945426941, 0.39215686917304993, 0.39215686917304993), (0.39915966987609863, 0.3960784375667572, 0.3960784375667572), (0.40336135029792786, 0.40000000596046448, 0.40000000596046448), (0.40756303071975708, 0.40392157435417175, 0.40392157435417175), (0.4117647111415863, 0.40784314274787903, 0.40784314274787903), (0.41596639156341553, 0.4117647111415863, 0.4117647111415863), (0.42016807198524475, 0.41568627953529358, 0.41568627953529358), (0.42436975240707397, 0.41960784792900085, 0.41960784792900085), (0.4285714328289032, 0.42352941632270813, 0.42352941632270813), (0.43277311325073242, 0.42745098471641541, 0.42745098471641541), (0.43697479367256165, 0.43137255311012268, 0.43137255311012268), (0.44117647409439087, 0.43529412150382996, 0.43529412150382996), (0.44537815451622009, 0.43921568989753723, 0.43921568989753723), (0.44957983493804932, 0.44313725829124451, 0.44313725829124451), (0.45378151535987854, 0.44705882668495178, 0.44705882668495178), (0.45798319578170776, 0.45098039507865906, 0.45098039507865906), (0.46218487620353699, 0.45882353186607361, 0.45882353186607361), (0.46638655662536621, 0.46274510025978088, 0.46274510025978088), (0.47058823704719543, 0.46666666865348816, 0.46666666865348816), (0.47478991746902466, 0.47058823704719543, 0.47058823704719543), (0.47899159789085388, 0.47450980544090271, 0.47450980544090271), (0.48319327831268311, 0.47843137383460999, 0.47843137383460999), (0.48739495873451233, 0.48235294222831726, 0.48235294222831726), (0.49159663915634155, 0.48627451062202454, 0.48627451062202454), (0.49579831957817078, 0.49019607901573181, 0.49019607901573181), (0.5, 0.49411764740943909, 0.49411764740943909), (0.50420171022415161, 0.50196081399917603, 0.50196081399917603), (0.50840336084365845, 0.5058823823928833, 0.5058823823928833), (0.51260507106781006, 0.50980395078659058, 0.50980395078659058), (0.51680672168731689, 0.51372551918029785, 0.51372551918029785), (0.52100843191146851, 0.51764708757400513, 0.51764708757400513), (0.52521008253097534, 0.5215686559677124, 0.5215686559677124), (0.52941179275512695, 0.52549022436141968, 0.52549022436141968), (0.53361344337463379, 0.52941179275512695, 0.52941179275512695), (0.5378151535987854, 0.53333336114883423, 0.53333336114883423), (0.54201680421829224, 0.5372549295425415, 0.5372549295425415), (0.54621851444244385, 0.54509806632995605, 0.54509806632995605), (0.55042016506195068, 0.54901963472366333, 0.54901963472366333), (0.55462187528610229, 0.55294120311737061, 0.55294120311737061), (0.55882352590560913, 0.55686277151107788, 0.55686277151107788), (0.56302523612976074, 0.56078433990478516, 0.56078433990478516), (0.56722688674926758, 0.56470590829849243, 0.56470590829849243), (0.57142859697341919, 0.56862747669219971, 0.56862747669219971), (0.57563024759292603, 0.57254904508590698, 0.57254904508590698), (0.57983195781707764, 0.57647061347961426, 0.57647061347961426), (0.58403360843658447, 0.58039218187332153, 0.58039218187332153), (0.58823531866073608, 0.58431375026702881, 0.58431375026702881), (0.59243696928024292, 0.58823531866073608, 0.58823531866073608), (0.59663867950439453, 0.59215688705444336, 0.59215688705444336), (0.60084033012390137, 0.59607845544815063, 0.59607845544815063), (0.60504204034805298, 0.60000002384185791, 0.60000002384185791), (0.60924369096755981, 0.60392159223556519, 0.60392159223556519), (0.61344540119171143, 0.60784316062927246, 0.60784316062927246), (0.61764705181121826, 0.61176472902297974, 0.61176472902297974), (0.62184876203536987, 0.61568629741668701, 0.61568629741668701), (0.62605041265487671, 0.61960786581039429, 0.61960786581039429), (0.63025212287902832, 0.62745100259780884, 0.62745100259780884), (0.63445377349853516, 0.63137257099151611, 0.63137257099151611), (0.63865548372268677, 0.63529413938522339, 0.63529413938522339), (0.6428571343421936, 0.63921570777893066, 0.63921570777893066), (0.64705884456634521, 0.64313727617263794, 0.64313727617263794), (0.65126049518585205, 0.64705884456634521, 0.64705884456634521), (0.65546220541000366, 0.65098041296005249, 0.65098041296005249), (0.6596638560295105, 0.65490198135375977, 0.65490198135375977), (0.66386556625366211, 0.65882354974746704, 0.65882354974746704), (0.66806721687316895, 0.66274511814117432, 0.66274511814117432), (0.67226892709732056, 0.66666668653488159, 0.66666668653488159), (0.67647057771682739, 0.67058825492858887, 0.67058825492858887), (0.680672287940979, 0.67450982332229614, 0.67450982332229614), (0.68487393856048584, 0.67843139171600342, 0.67843139171600342), (0.68907564878463745, 0.68235296010971069, 0.68235296010971069), (0.69327729940414429, 0.68627452850341797, 0.68627452850341797), (0.6974790096282959, 0.69019609689712524, 0.69019609689712524), (0.70168066024780273, 0.69411766529083252, 0.69411766529083252), (0.70588237047195435, 0.69803923368453979, 0.69803923368453979), (0.71008402109146118, 0.70196080207824707, 0.70196080207824707), (0.71428573131561279, 0.70980393886566162, 0.70980393886566162), (0.71848738193511963, 0.7137255072593689, 0.7137255072593689), (0.72268909215927124, 0.71764707565307617, 0.71764707565307617), (0.72689074277877808, 0.72156864404678345, 0.72156864404678345), (0.73109245300292969, 0.72549021244049072, 0.72549021244049072), (0.73529410362243652, 0.729411780834198, 0.729411780834198), (0.73949581384658813, 0.73333334922790527, 0.73333334922790527), (0.74369746446609497, 0.73725491762161255, 0.73725491762161255), (0.74789917469024658, 0.74117648601531982, 0.74117648601531982), (0.75210082530975342, 0.7450980544090271, 0.7450980544090271), (0.75630253553390503, 0.75294119119644165, 0.75294119119644165), (0.76050418615341187, 0.75686275959014893, 0.75686275959014893), (0.76470589637756348, 0.7607843279838562, 0.7607843279838562), (0.76890754699707031, 0.76470589637756348, 0.76470589637756348), (0.77310925722122192, 0.76862746477127075, 0.76862746477127075), (0.77731090784072876, 0.77254903316497803, 0.77254903316497803), (0.78151261806488037, 0.7764706015586853, 0.7764706015586853), (0.78571426868438721, 0.78039216995239258, 0.78039216995239258), (0.78991597890853882, 0.78431373834609985, 0.78431373834609985), (0.79411762952804565, 0.78823530673980713, 0.78823530673980713), (0.79831933975219727, 0.79607844352722168, 0.79607844352722168), (0.8025209903717041, 0.80000001192092896, 0.80000001192092896), (0.80672270059585571, 0.80392158031463623, 0.80392158031463623), (0.81092435121536255, 0.80784314870834351, 0.80784314870834351), (0.81512606143951416, 0.81176471710205078, 0.81176471710205078), (0.819327712059021, 0.81568628549575806, 0.81568628549575806), (0.82352942228317261, 0.81960785388946533, 0.81960785388946533), (0.82773107290267944, 0.82352942228317261, 0.82352942228317261), (0.83193278312683105, 0.82745099067687988, 0.82745099067687988), (0.83613443374633789, 0.83137255907058716, 0.83137255907058716), (0.8403361439704895, 0.83529412746429443, 0.83529412746429443), (0.84453779458999634, 0.83921569585800171, 0.83921569585800171), (0.84873950481414795, 0.84313726425170898, 0.84313726425170898), (0.85294115543365479, 0.84705883264541626, 0.84705883264541626), (0.8571428656578064, 0.85098040103912354, 0.85098040103912354), (0.86134451627731323, 0.85490196943283081, 0.85490196943283081), (0.86554622650146484, 0.85882353782653809, 0.85882353782653809), (0.86974787712097168, 0.86274510622024536, 0.86274510622024536), (0.87394958734512329, 0.86666667461395264, 0.86666667461395264), (0.87815123796463013, 0.87058824300765991, 0.87058824300765991), (0.88235294818878174, 0.87843137979507446, 0.87843137979507446), (0.88655459880828857, 0.88235294818878174, 0.88235294818878174), (0.89075630903244019, 0.88627451658248901, 0.88627451658248901), (0.89495795965194702, 0.89019608497619629, 0.89019608497619629), (0.89915966987609863, 0.89411765336990356, 0.89411765336990356), (0.90336132049560547, 0.89803922176361084, 0.89803922176361084), (0.90756303071975708, 0.90196079015731812, 0.90196079015731812), (0.91176468133926392, 0.90588235855102539, 0.90588235855102539), (0.91596639156341553, 0.90980392694473267, 0.90980392694473267), (0.92016804218292236, 0.91372549533843994, 0.91372549533843994), (0.92436975240707397, 0.91764706373214722, 0.91764706373214722), (0.92857140302658081, 0.92156863212585449, 0.92156863212585449), (0.93277311325073242, 0.92549020051956177, 0.92549020051956177), (0.93697476387023926, 0.92941176891326904, 0.92941176891326904), (0.94117647409439087, 0.93333333730697632, 0.93333333730697632), (0.94537812471389771, 0.93725490570068359, 0.93725490570068359), (0.94957983493804932, 0.94117647409439087, 0.94117647409439087), (0.95378148555755615, 0.94509804248809814, 0.94509804248809814), (0.95798319578170776, 0.94901961088180542, 0.94901961088180542), (0.9621848464012146, 0.9529411792755127, 0.9529411792755127), (0.96638655662536621, 0.96078431606292725, 0.96078431606292725), (0.97058820724487305, 0.96470588445663452, 0.96470588445663452), (0.97478991746902466, 0.9686274528503418, 0.9686274528503418), (0.97899156808853149, 0.97254902124404907, 0.97254902124404907), (0.98319327831268311, 0.97647058963775635, 0.97647058963775635), (0.98739492893218994, 0.98039215803146362, 0.98039215803146362), (0.99159663915634155, 0.9843137264251709, 0.9843137264251709), (0.99579828977584839, 0.98823529481887817, 0.98823529481887817), (1.0, 0.99215686321258545, 0.99215686321258545)], green = [(0.0, 0.0, 0.0), (0.0042016808874905109, 0.0039215688593685627, 0.0039215688593685627), (0.0084033617749810219, 0.0078431377187371254, 0.0078431377187371254), (0.012605042196810246, 0.011764706112444401, 0.011764706112444401), (0.016806723549962044, 0.015686275437474251, 0.015686275437474251), (0.021008403971791267, 0.019607843831181526, 0.019607843831181526), (0.025210084393620491, 0.023529412224888802, 0.023529412224888802), (0.029411764815449715, 0.027450980618596077, 0.027450980618596077), (0.033613447099924088, 0.031372550874948502, 0.031372550874948502), (0.037815127521753311, 0.035294119268655777, 0.035294119268655777), (0.042016807943582535, 0.043137256056070328, 0.043137256056070328), (0.046218488365411758, 0.047058824449777603, 0.047058824449777603), (0.050420168787240982, 0.050980392843484879, 0.050980392843484879), (0.054621849209070206, 0.054901961237192154, 0.054901961237192154), (0.058823529630899429, 0.058823529630899429, 0.058823529630899429), (0.063025213778018951, 0.062745101749897003, 0.062745101749897003), (0.067226894199848175, 0.066666670143604279, 0.066666670143604279), (0.071428574621677399, 0.070588238537311554, 0.070588238537311554), (0.075630255043506622, 0.074509806931018829, 0.074509806931018829), (0.079831935465335846, 0.078431375324726105, 0.078431375324726105), (0.08403361588716507, 0.08235294371843338, 0.08235294371843338), (0.088235296308994293, 0.086274512112140656, 0.086274512112140656), (0.092436976730823517, 0.090196080505847931, 0.090196080505847931), (0.09663865715265274, 0.094117648899555206, 0.094117648899555206), (0.10084033757448196, 0.098039217293262482, 0.098039217293262482), (0.10504201799631119, 0.10196078568696976, 0.10196078568696976), (0.10924369841814041, 0.10588235408067703, 0.10588235408067703), (0.11344537883996964, 0.10980392247438431, 0.10980392247438431), (0.11764705926179886, 0.11372549086809158, 0.11372549086809158), (0.12184873968362808, 0.11764705926179886, 0.11764705926179886), (0.1260504275560379, 0.12549020349979401, 0.12549020349979401), (0.13025210797786713, 0.12941177189350128, 0.12941177189350128), (0.13445378839969635, 0.13333334028720856, 0.13333334028720856), (0.13865546882152557, 0.13725490868091583, 0.13725490868091583), (0.1428571492433548, 0.14117647707462311, 0.14117647707462311), (0.14705882966518402, 0.14509804546833038, 0.14509804546833038), (0.15126051008701324, 0.14901961386203766, 0.14901961386203766), (0.15546219050884247, 0.15294118225574493, 0.15294118225574493), (0.15966387093067169, 0.15686275064945221, 0.15686275064945221), (0.16386555135250092, 0.16078431904315948, 0.16078431904315948), (0.16806723177433014, 0.16470588743686676, 0.16470588743686676), (0.17226891219615936, 0.16862745583057404, 0.16862745583057404), (0.17647059261798859, 0.17254902422428131, 0.17254902422428131), (0.18067227303981781, 0.17647059261798859, 0.17647059261798859), (0.18487395346164703, 0.18039216101169586, 0.18039216101169586), (0.18907563388347626, 0.18431372940540314, 0.18431372940540314), (0.19327731430530548, 0.18823529779911041, 0.18823529779911041), (0.1974789947271347, 0.19215686619281769, 0.19215686619281769), (0.20168067514896393, 0.19607843458652496, 0.19607843458652496), (0.20588235557079315, 0.20000000298023224, 0.20000000298023224), (0.21008403599262238, 0.20784313976764679, 0.20784313976764679), (0.2142857164144516, 0.21176470816135406, 0.21176470816135406), (0.21848739683628082, 0.21568627655506134, 0.21568627655506134), (0.22268907725811005, 0.21960784494876862, 0.21960784494876862), (0.22689075767993927, 0.22352941334247589, 0.22352941334247589), (0.23109243810176849, 0.22745098173618317, 0.22745098173618317), (0.23529411852359772, 0.23137255012989044, 0.23137255012989044), (0.23949579894542694, 0.23529411852359772, 0.23529411852359772), (0.24369747936725616, 0.23921568691730499, 0.23921568691730499), (0.24789915978908539, 0.24313725531101227, 0.24313725531101227), (0.25210085511207581, 0.25098040699958801, 0.25098040699958801), (0.25630253553390503, 0.25490197539329529, 0.25490197539329529), (0.26050421595573425, 0.25882354378700256, 0.25882354378700256), (0.26470589637756348, 0.26274511218070984, 0.26274511218070984), (0.2689075767993927, 0.26666668057441711, 0.26666668057441711), (0.27310925722122192, 0.27058824896812439, 0.27058824896812439), (0.27731093764305115, 0.27450981736183167, 0.27450981736183167), (0.28151261806488037, 0.27843138575553894, 0.27843138575553894), (0.28571429848670959, 0.28235295414924622, 0.28235295414924622), (0.28991597890853882, 0.28627452254295349, 0.28627452254295349), (0.29411765933036804, 0.29411765933036804, 0.29411765933036804), (0.29831933975219727, 0.29803922772407532, 0.29803922772407532), (0.30252102017402649, 0.30196079611778259, 0.30196079611778259), (0.30672270059585571, 0.30588236451148987, 0.30588236451148987), (0.31092438101768494, 0.30980393290519714, 0.30980393290519714), (0.31512606143951416, 0.31372550129890442, 0.31372550129890442), (0.31932774186134338, 0.31764706969261169, 0.31764706969261169), (0.32352942228317261, 0.32156863808631897, 0.32156863808631897), (0.32773110270500183, 0.32549020648002625, 0.32549020648002625), (0.33193278312683105, 0.32941177487373352, 0.32941177487373352), (0.33613446354866028, 0.3333333432674408, 0.3333333432674408), (0.3403361439704895, 0.33725491166114807, 0.33725491166114807), (0.34453782439231873, 0.34117648005485535, 0.34117648005485535), (0.34873950481414795, 0.34509804844856262, 0.34509804844856262), (0.35294118523597717, 0.3490196168422699, 0.3490196168422699), (0.3571428656578064, 0.35294118523597717, 0.35294118523597717), (0.36134454607963562, 0.35686275362968445, 0.35686275362968445), (0.36554622650146484, 0.36078432202339172, 0.36078432202339172), (0.36974790692329407, 0.364705890417099, 0.364705890417099), (0.37394958734512329, 0.36862745881080627, 0.36862745881080627), (0.37815126776695251, 0.37647059559822083, 0.37647059559822083), (0.38235294818878174, 0.3803921639919281, 0.3803921639919281), (0.38655462861061096, 0.38431373238563538, 0.38431373238563538), (0.39075630903244019, 0.38823530077934265, 0.38823530077934265), (0.39495798945426941, 0.39215686917304993, 0.39215686917304993), (0.39915966987609863, 0.3960784375667572, 0.3960784375667572), (0.40336135029792786, 0.40000000596046448, 0.40000000596046448), (0.40756303071975708, 0.40392157435417175, 0.40392157435417175), (0.4117647111415863, 0.40784314274787903, 0.40784314274787903), (0.41596639156341553, 0.4117647111415863, 0.4117647111415863), (0.42016807198524475, 0.41568627953529358, 0.41568627953529358), (0.42436975240707397, 0.41960784792900085, 0.41960784792900085), (0.4285714328289032, 0.42352941632270813, 0.42352941632270813), (0.43277311325073242, 0.42745098471641541, 0.42745098471641541), (0.43697479367256165, 0.43137255311012268, 0.43137255311012268), (0.44117647409439087, 0.43529412150382996, 0.43529412150382996), (0.44537815451622009, 0.43921568989753723, 0.43921568989753723), (0.44957983493804932, 0.44313725829124451, 0.44313725829124451), (0.45378151535987854, 0.44705882668495178, 0.44705882668495178), (0.45798319578170776, 0.45098039507865906, 0.45098039507865906), (0.46218487620353699, 0.45882353186607361, 0.45882353186607361), (0.46638655662536621, 0.46274510025978088, 0.46274510025978088), (0.47058823704719543, 0.46666666865348816, 0.46666666865348816), (0.47478991746902466, 0.47058823704719543, 0.47058823704719543), (0.47899159789085388, 0.47450980544090271, 0.47450980544090271), (0.48319327831268311, 0.47843137383460999, 0.47843137383460999), (0.48739495873451233, 0.48235294222831726, 0.48235294222831726), (0.49159663915634155, 0.48627451062202454, 0.48627451062202454), (0.49579831957817078, 0.49019607901573181, 0.49019607901573181), (0.5, 0.49411764740943909, 0.49411764740943909), (0.50420171022415161, 0.50196081399917603, 0.50196081399917603), (0.50840336084365845, 0.5058823823928833, 0.5058823823928833), (0.51260507106781006, 0.50980395078659058, 0.50980395078659058), (0.51680672168731689, 0.51372551918029785, 0.51372551918029785), (0.52100843191146851, 0.51764708757400513, 0.51764708757400513), (0.52521008253097534, 0.5215686559677124, 0.5215686559677124), (0.52941179275512695, 0.52549022436141968, 0.52549022436141968), (0.53361344337463379, 0.52941179275512695, 0.52941179275512695), (0.5378151535987854, 0.53333336114883423, 0.53333336114883423), (0.54201680421829224, 0.5372549295425415, 0.5372549295425415), (0.54621851444244385, 0.54509806632995605, 0.54509806632995605), (0.55042016506195068, 0.54901963472366333, 0.54901963472366333), (0.55462187528610229, 0.55294120311737061, 0.55294120311737061), (0.55882352590560913, 0.55686277151107788, 0.55686277151107788), (0.56302523612976074, 0.56078433990478516, 0.56078433990478516), (0.56722688674926758, 0.56470590829849243, 0.56470590829849243), (0.57142859697341919, 0.56862747669219971, 0.56862747669219971), (0.57563024759292603, 0.57254904508590698, 0.57254904508590698), (0.57983195781707764, 0.57647061347961426, 0.57647061347961426), (0.58403360843658447, 0.58039218187332153, 0.58039218187332153), (0.58823531866073608, 0.58431375026702881, 0.58431375026702881), (0.59243696928024292, 0.58823531866073608, 0.58823531866073608), (0.59663867950439453, 0.59215688705444336, 0.59215688705444336), (0.60084033012390137, 0.59607845544815063, 0.59607845544815063), (0.60504204034805298, 0.60000002384185791, 0.60000002384185791), (0.60924369096755981, 0.60392159223556519, 0.60392159223556519), (0.61344540119171143, 0.60784316062927246, 0.60784316062927246), (0.61764705181121826, 0.61176472902297974, 0.61176472902297974), (0.62184876203536987, 0.61568629741668701, 0.61568629741668701), (0.62605041265487671, 0.61960786581039429, 0.61960786581039429), (0.63025212287902832, 0.62745100259780884, 0.62745100259780884), (0.63445377349853516, 0.63137257099151611, 0.63137257099151611), (0.63865548372268677, 0.63529413938522339, 0.63529413938522339), (0.6428571343421936, 0.63921570777893066, 0.63921570777893066), (0.64705884456634521, 0.64313727617263794, 0.64313727617263794), (0.65126049518585205, 0.64705884456634521, 0.64705884456634521), (0.65546220541000366, 0.65098041296005249, 0.65098041296005249), (0.6596638560295105, 0.65490198135375977, 0.65490198135375977), (0.66386556625366211, 0.65882354974746704, 0.65882354974746704), (0.66806721687316895, 0.66274511814117432, 0.66274511814117432), (0.67226892709732056, 0.66666668653488159, 0.66666668653488159), (0.67647057771682739, 0.67058825492858887, 0.67058825492858887), (0.680672287940979, 0.67450982332229614, 0.67450982332229614), (0.68487393856048584, 0.67843139171600342, 0.67843139171600342), (0.68907564878463745, 0.68235296010971069, 0.68235296010971069), (0.69327729940414429, 0.68627452850341797, 0.68627452850341797), (0.6974790096282959, 0.69019609689712524, 0.69019609689712524), (0.70168066024780273, 0.69411766529083252, 0.69411766529083252), (0.70588237047195435, 0.69803923368453979, 0.69803923368453979), (0.71008402109146118, 0.70196080207824707, 0.70196080207824707), (0.71428573131561279, 0.70980393886566162, 0.70980393886566162), (0.71848738193511963, 0.7137255072593689, 0.7137255072593689), (0.72268909215927124, 0.71764707565307617, 0.71764707565307617), (0.72689074277877808, 0.72156864404678345, 0.72156864404678345), (0.73109245300292969, 0.72549021244049072, 0.72549021244049072), (0.73529410362243652, 0.729411780834198, 0.729411780834198), (0.73949581384658813, 0.73333334922790527, 0.73333334922790527), (0.74369746446609497, 0.73725491762161255, 0.73725491762161255), (0.74789917469024658, 0.74117648601531982, 0.74117648601531982), (0.75210082530975342, 0.7450980544090271, 0.7450980544090271), (0.75630253553390503, 0.75294119119644165, 0.75294119119644165), (0.76050418615341187, 0.75686275959014893, 0.75686275959014893), (0.76470589637756348, 0.7607843279838562, 0.7607843279838562), (0.76890754699707031, 0.76470589637756348, 0.76470589637756348), (0.77310925722122192, 0.76862746477127075, 0.76862746477127075), (0.77731090784072876, 0.77254903316497803, 0.77254903316497803), (0.78151261806488037, 0.7764706015586853, 0.7764706015586853), (0.78571426868438721, 0.78039216995239258, 0.78039216995239258), (0.78991597890853882, 0.78431373834609985, 0.78431373834609985), (0.79411762952804565, 0.78823530673980713, 0.78823530673980713), (0.79831933975219727, 0.79607844352722168, 0.79607844352722168), (0.8025209903717041, 0.80000001192092896, 0.80000001192092896), (0.80672270059585571, 0.80392158031463623, 0.80392158031463623), (0.81092435121536255, 0.80784314870834351, 0.80784314870834351), (0.81512606143951416, 0.81176471710205078, 0.81176471710205078), (0.819327712059021, 0.81568628549575806, 0.81568628549575806), (0.82352942228317261, 0.81960785388946533, 0.81960785388946533), (0.82773107290267944, 0.82352942228317261, 0.82352942228317261), (0.83193278312683105, 0.82745099067687988, 0.82745099067687988), (0.83613443374633789, 0.83137255907058716, 0.83137255907058716), (0.8403361439704895, 0.83529412746429443, 0.83529412746429443), (0.84453779458999634, 0.83921569585800171, 0.83921569585800171), (0.84873950481414795, 0.84313726425170898, 0.84313726425170898), (0.85294115543365479, 0.84705883264541626, 0.84705883264541626), (0.8571428656578064, 0.85098040103912354, 0.85098040103912354), (0.86134451627731323, 0.85490196943283081, 0.85490196943283081), (0.86554622650146484, 0.85882353782653809, 0.85882353782653809), (0.86974787712097168, 0.86274510622024536, 0.86274510622024536), (0.87394958734512329, 0.86666667461395264, 0.86666667461395264), (0.87815123796463013, 0.87058824300765991, 0.87058824300765991), (0.88235294818878174, 0.87843137979507446, 0.87843137979507446), (0.88655459880828857, 0.88235294818878174, 0.88235294818878174), (0.89075630903244019, 0.88627451658248901, 0.88627451658248901), (0.89495795965194702, 0.89019608497619629, 0.89019608497619629), (0.89915966987609863, 0.89411765336990356, 0.89411765336990356), (0.90336132049560547, 0.89803922176361084, 0.89803922176361084), (0.90756303071975708, 0.90196079015731812, 0.90196079015731812), (0.91176468133926392, 0.90588235855102539, 0.90588235855102539), (0.91596639156341553, 0.90980392694473267, 0.90980392694473267), (0.92016804218292236, 0.91372549533843994, 0.91372549533843994), (0.92436975240707397, 0.91764706373214722, 0.91764706373214722), (0.92857140302658081, 0.92156863212585449, 0.92156863212585449), (0.93277311325073242, 0.92549020051956177, 0.92549020051956177), (0.93697476387023926, 0.92941176891326904, 0.92941176891326904), (0.94117647409439087, 0.93333333730697632, 0.93333333730697632), (0.94537812471389771, 0.93725490570068359, 0.93725490570068359), (0.94957983493804932, 0.94117647409439087, 0.94117647409439087), (0.95378148555755615, 0.94509804248809814, 0.94509804248809814), (0.95798319578170776, 0.94901961088180542, 0.94901961088180542), (0.9621848464012146, 0.9529411792755127, 0.9529411792755127), (0.96638655662536621, 0.96078431606292725, 0.96078431606292725), (0.97058820724487305, 0.96470588445663452, 0.96470588445663452), (0.97478991746902466, 0.9686274528503418, 0.9686274528503418), (0.97899156808853149, 0.97254902124404907, 0.97254902124404907), (0.98319327831268311, 0.97647058963775635, 0.97647058963775635), (0.98739492893218994, 0.98039215803146362, 0.98039215803146362), (0.99159663915634155, 0.9843137264251709, 0.9843137264251709), (0.99579828977584839, 0.98823529481887817, 0.98823529481887817), (1.0, 0.99215686321258545, 0.99215686321258545)], blue = [(0.0, 0.0, 0.0), (0.0042016808874905109, 0.0039215688593685627, 0.0039215688593685627), (0.0084033617749810219, 0.011764706112444401, 0.011764706112444401), (0.012605042196810246, 0.019607843831181526, 0.019607843831181526), (0.016806723549962044, 0.027450980618596077, 0.027450980618596077), (0.021008403971791267, 0.035294119268655777, 0.035294119268655777), (0.025210084393620491, 0.043137256056070328, 0.043137256056070328), (0.029411764815449715, 0.050980392843484879, 0.050980392843484879), (0.033613447099924088, 0.058823529630899429, 0.058823529630899429), (0.037815127521753311, 0.066666670143604279, 0.066666670143604279), (0.042016807943582535, 0.08235294371843338, 0.08235294371843338), (0.046218488365411758, 0.090196080505847931, 0.090196080505847931), (0.050420168787240982, 0.098039217293262482, 0.098039217293262482), (0.054621849209070206, 0.10588235408067703, 0.10588235408067703), (0.058823529630899429, 0.11372549086809158, 0.11372549086809158), (0.063025213778018951, 0.12156862765550613, 0.12156862765550613), (0.067226894199848175, 0.12941177189350128, 0.12941177189350128), (0.071428574621677399, 0.13725490868091583, 0.13725490868091583), (0.075630255043506622, 0.14509804546833038, 0.14509804546833038), (0.079831935465335846, 0.15294118225574493, 0.15294118225574493), (0.08403361588716507, 0.16078431904315948, 0.16078431904315948), (0.088235296308994293, 0.16862745583057404, 0.16862745583057404), (0.092436976730823517, 0.17647059261798859, 0.17647059261798859), (0.09663865715265274, 0.18431372940540314, 0.18431372940540314), (0.10084033757448196, 0.19215686619281769, 0.19215686619281769), (0.10504201799631119, 0.20000000298023224, 0.20000000298023224), (0.10924369841814041, 0.20784313976764679, 0.20784313976764679), (0.11344537883996964, 0.21568627655506134, 0.21568627655506134), (0.11764705926179886, 0.22352941334247589, 0.22352941334247589), (0.12184873968362808, 0.23137255012989044, 0.23137255012989044), (0.1260504275560379, 0.24705882370471954, 0.24705882370471954), (0.13025210797786713, 0.25490197539329529, 0.25490197539329529), (0.13445378839969635, 0.26274511218070984, 0.26274511218070984), (0.13865546882152557, 0.27058824896812439, 0.27058824896812439), (0.1428571492433548, 0.27843138575553894, 0.27843138575553894), (0.14705882966518402, 0.28627452254295349, 0.28627452254295349), (0.15126051008701324, 0.29411765933036804, 0.29411765933036804), (0.15546219050884247, 0.30196079611778259, 0.30196079611778259), (0.15966387093067169, 0.30980393290519714, 0.30980393290519714), (0.16386555135250092, 0.31764706969261169, 0.31764706969261169), (0.16806723177433014, 0.32549020648002625, 0.32549020648002625), (0.17226891219615936, 0.3333333432674408, 0.3333333432674408), (0.17647059261798859, 0.34117648005485535, 0.34117648005485535), (0.18067227303981781, 0.3490196168422699, 0.3490196168422699), (0.18487395346164703, 0.35686275362968445, 0.35686275362968445), (0.18907563388347626, 0.364705890417099, 0.364705890417099), (0.19327731430530548, 0.37254902720451355, 0.37254902720451355), (0.1974789947271347, 0.3803921639919281, 0.3803921639919281), (0.20168067514896393, 0.38823530077934265, 0.38823530077934265), (0.20588235557079315, 0.3960784375667572, 0.3960784375667572), (0.21008403599262238, 0.4117647111415863, 0.4117647111415863), (0.2142857164144516, 0.41960784792900085, 0.41960784792900085), (0.21848739683628082, 0.42745098471641541, 0.42745098471641541), (0.22268907725811005, 0.43529412150382996, 0.43529412150382996), (0.22689075767993927, 0.44313725829124451, 0.44313725829124451), (0.23109243810176849, 0.45098039507865906, 0.45098039507865906), (0.23529411852359772, 0.45882353186607361, 0.45882353186607361), (0.23949579894542694, 0.46666666865348816, 0.46666666865348816), (0.24369747936725616, 0.47450980544090271, 0.47450980544090271), (0.24789915978908539, 0.48235294222831726, 0.48235294222831726), (0.25210085511207581, 0.49803921580314636, 0.49803921580314636), (0.25630253553390503, 0.5058823823928833, 0.5058823823928833), (0.26050421595573425, 0.51372551918029785, 0.51372551918029785), (0.26470589637756348, 0.5215686559677124, 0.5215686559677124), (0.2689075767993927, 0.52941179275512695, 0.52941179275512695), (0.27310925722122192, 0.5372549295425415, 0.5372549295425415), (0.27731093764305115, 0.54509806632995605, 0.54509806632995605), (0.28151261806488037, 0.55294120311737061, 0.55294120311737061), (0.28571429848670959, 0.56078433990478516, 0.56078433990478516), (0.28991597890853882, 0.56862747669219971, 0.56862747669219971), (0.29411765933036804, 0.58431375026702881, 0.58431375026702881), (0.29831933975219727, 0.59215688705444336, 0.59215688705444336), (0.30252102017402649, 0.60000002384185791, 0.60000002384185791), (0.30672270059585571, 0.60784316062927246, 0.60784316062927246), (0.31092438101768494, 0.61568629741668701, 0.61568629741668701), (0.31512606143951416, 0.62352943420410156, 0.62352943420410156), (0.31932774186134338, 0.63137257099151611, 0.63137257099151611), (0.32352942228317261, 0.63921570777893066, 0.63921570777893066), (0.32773110270500183, 0.64705884456634521, 0.64705884456634521), (0.33193278312683105, 0.65490198135375977, 0.65490198135375977), (0.33613446354866028, 0.66274511814117432, 0.66274511814117432), (0.3403361439704895, 0.67058825492858887, 0.67058825492858887), (0.34453782439231873, 0.67843139171600342, 0.67843139171600342), (0.34873950481414795, 0.68627452850341797, 0.68627452850341797), (0.35294118523597717, 0.69411766529083252, 0.69411766529083252), (0.3571428656578064, 0.70196080207824707, 0.70196080207824707), (0.36134454607963562, 0.70980393886566162, 0.70980393886566162), (0.36554622650146484, 0.71764707565307617, 0.71764707565307617), (0.36974790692329407, 0.72549021244049072, 0.72549021244049072), (0.37394958734512329, 0.73333334922790527, 0.73333334922790527), (0.37815126776695251, 0.74901962280273438, 0.74901962280273438), (0.38235294818878174, 0.75686275959014893, 0.75686275959014893), (0.38655462861061096, 0.76470589637756348, 0.76470589637756348), (0.39075630903244019, 0.77254903316497803, 0.77254903316497803), (0.39495798945426941, 0.78039216995239258, 0.78039216995239258), (0.39915966987609863, 0.78823530673980713, 0.78823530673980713), (0.40336135029792786, 0.79607844352722168, 0.79607844352722168), (0.40756303071975708, 0.80392158031463623, 0.80392158031463623), (0.4117647111415863, 0.81176471710205078, 0.81176471710205078), (0.41596639156341553, 0.81960785388946533, 0.81960785388946533), (0.42016807198524475, 0.82745099067687988, 0.82745099067687988), (0.42436975240707397, 0.83529412746429443, 0.83529412746429443), (0.4285714328289032, 0.84313726425170898, 0.84313726425170898), (0.43277311325073242, 0.85098040103912354, 0.85098040103912354), (0.43697479367256165, 0.85882353782653809, 0.85882353782653809), (0.44117647409439087, 0.86666667461395264, 0.86666667461395264), (0.44537815451622009, 0.87450981140136719, 0.87450981140136719), (0.44957983493804932, 0.88235294818878174, 0.88235294818878174), (0.45378151535987854, 0.89019608497619629, 0.89019608497619629), (0.45798319578170776, 0.89803922176361084, 0.89803922176361084), (0.46218487620353699, 0.91372549533843994, 0.91372549533843994), (0.46638655662536621, 0.92156863212585449, 0.92156863212585449), (0.47058823704719543, 0.92941176891326904, 0.92941176891326904), (0.47478991746902466, 0.93725490570068359, 0.93725490570068359), (0.47899159789085388, 0.94509804248809814, 0.94509804248809814), (0.48319327831268311, 0.9529411792755127, 0.9529411792755127), (0.48739495873451233, 0.96078431606292725, 0.96078431606292725), (0.49159663915634155, 0.9686274528503418, 0.9686274528503418), (0.49579831957817078, 0.97647058963775635, 0.97647058963775635), (0.5, 0.9843137264251709, 0.9843137264251709), (0.50420171022415161, 1.0, 1.0), (0.50840336084365845, 0.9843137264251709, 0.9843137264251709), (0.51260507106781006, 0.9686274528503418, 0.9686274528503418), (0.51680672168731689, 0.9529411792755127, 0.9529411792755127), (0.52100843191146851, 0.93333333730697632, 0.93333333730697632), (0.52521008253097534, 0.91764706373214722, 0.91764706373214722), (0.52941179275512695, 0.90196079015731812, 0.90196079015731812), (0.53361344337463379, 0.88627451658248901, 0.88627451658248901), (0.5378151535987854, 0.86666667461395264, 0.86666667461395264), (0.54201680421829224, 0.85098040103912354, 0.85098040103912354), (0.54621851444244385, 0.81960785388946533, 0.81960785388946533), (0.55042016506195068, 0.80000001192092896, 0.80000001192092896), (0.55462187528610229, 0.78431373834609985, 0.78431373834609985), (0.55882352590560913, 0.76862746477127075, 0.76862746477127075), (0.56302523612976074, 0.75294119119644165, 0.75294119119644165), (0.56722688674926758, 0.73333334922790527, 0.73333334922790527), (0.57142859697341919, 0.71764707565307617, 0.71764707565307617), (0.57563024759292603, 0.70196080207824707, 0.70196080207824707), (0.57983195781707764, 0.68627452850341797, 0.68627452850341797), (0.58403360843658447, 0.66666668653488159, 0.66666668653488159), (0.58823531866073608, 0.65098041296005249, 0.65098041296005249), (0.59243696928024292, 0.63529413938522339, 0.63529413938522339), (0.59663867950439453, 0.61960786581039429, 0.61960786581039429), (0.60084033012390137, 0.60000002384185791, 0.60000002384185791), (0.60504204034805298, 0.58431375026702881, 0.58431375026702881), (0.60924369096755981, 0.56862747669219971, 0.56862747669219971), (0.61344540119171143, 0.55294120311737061, 0.55294120311737061), (0.61764705181121826, 0.53333336114883423, 0.53333336114883423), (0.62184876203536987, 0.51764708757400513, 0.51764708757400513), (0.62605041265487671, 0.50196081399917603, 0.50196081399917603), (0.63025212287902832, 0.46666666865348816, 0.46666666865348816), (0.63445377349853516, 0.45098039507865906, 0.45098039507865906), (0.63865548372268677, 0.43529412150382996, 0.43529412150382996), (0.6428571343421936, 0.41960784792900085, 0.41960784792900085), (0.64705884456634521, 0.40000000596046448, 0.40000000596046448), (0.65126049518585205, 0.38431373238563538, 0.38431373238563538), (0.65546220541000366, 0.36862745881080627, 0.36862745881080627), (0.6596638560295105, 0.35294118523597717, 0.35294118523597717), (0.66386556625366211, 0.3333333432674408, 0.3333333432674408), (0.66806721687316895, 0.31764706969261169, 0.31764706969261169), (0.67226892709732056, 0.30196079611778259, 0.30196079611778259), (0.67647057771682739, 0.28627452254295349, 0.28627452254295349), (0.680672287940979, 0.26666668057441711, 0.26666668057441711), (0.68487393856048584, 0.25098040699958801, 0.25098040699958801), (0.68907564878463745, 0.23529411852359772, 0.23529411852359772), (0.69327729940414429, 0.21960784494876862, 0.21960784494876862), (0.6974790096282959, 0.20000000298023224, 0.20000000298023224), (0.70168066024780273, 0.18431372940540314, 0.18431372940540314), (0.70588237047195435, 0.16862745583057404, 0.16862745583057404), (0.71008402109146118, 0.15294118225574493, 0.15294118225574493), (0.71428573131561279, 0.11764705926179886, 0.11764705926179886), (0.71848738193511963, 0.10196078568696976, 0.10196078568696976), (0.72268909215927124, 0.086274512112140656, 0.086274512112140656), (0.72689074277877808, 0.066666670143604279, 0.066666670143604279), (0.73109245300292969, 0.050980392843484879, 0.050980392843484879), (0.73529410362243652, 0.035294119268655777, 0.035294119268655777), (0.73949581384658813, 0.019607843831181526, 0.019607843831181526), (0.74369746446609497, 0.0, 0.0), (0.74789917469024658, 0.011764706112444401, 0.011764706112444401), (0.75210082530975342, 0.027450980618596077, 0.027450980618596077), (0.75630253553390503, 0.058823529630899429, 0.058823529630899429), (0.76050418615341187, 0.074509806931018829, 0.074509806931018829), (0.76470589637756348, 0.086274512112140656, 0.086274512112140656), (0.76890754699707031, 0.10196078568696976, 0.10196078568696976), (0.77310925722122192, 0.11764705926179886, 0.11764705926179886), (0.77731090784072876, 0.13333334028720856, 0.13333334028720856), (0.78151261806488037, 0.14901961386203766, 0.14901961386203766), (0.78571426868438721, 0.16078431904315948, 0.16078431904315948), (0.78991597890853882, 0.17647059261798859, 0.17647059261798859), (0.79411762952804565, 0.19215686619281769, 0.19215686619281769), (0.79831933975219727, 0.22352941334247589, 0.22352941334247589), (0.8025209903717041, 0.23529411852359772, 0.23529411852359772), (0.80672270059585571, 0.25098040699958801, 0.25098040699958801), (0.81092435121536255, 0.26666668057441711, 0.26666668057441711), (0.81512606143951416, 0.28235295414924622, 0.28235295414924622), (0.819327712059021, 0.29803922772407532, 0.29803922772407532), (0.82352942228317261, 0.30980393290519714, 0.30980393290519714), (0.82773107290267944, 0.32549020648002625, 0.32549020648002625), (0.83193278312683105, 0.34117648005485535, 0.34117648005485535), (0.83613443374633789, 0.35686275362968445, 0.35686275362968445), (0.8403361439704895, 0.37254902720451355, 0.37254902720451355), (0.84453779458999634, 0.38431373238563538, 0.38431373238563538), (0.84873950481414795, 0.40000000596046448, 0.40000000596046448), (0.85294115543365479, 0.41568627953529358, 0.41568627953529358), (0.8571428656578064, 0.43137255311012268, 0.43137255311012268), (0.86134451627731323, 0.44705882668495178, 0.44705882668495178), (0.86554622650146484, 0.45882353186607361, 0.45882353186607361), (0.86974787712097168, 0.47450980544090271, 0.47450980544090271), (0.87394958734512329, 0.49019607901573181, 0.49019607901573181), (0.87815123796463013, 0.5058823823928833, 0.5058823823928833), (0.88235294818878174, 0.5372549295425415, 0.5372549295425415), (0.88655459880828857, 0.54901963472366333, 0.54901963472366333), (0.89075630903244019, 0.56470590829849243, 0.56470590829849243), (0.89495795965194702, 0.58039218187332153, 0.58039218187332153), (0.89915966987609863, 0.59607845544815063, 0.59607845544815063), (0.90336132049560547, 0.61176472902297974, 0.61176472902297974), (0.90756303071975708, 0.62352943420410156, 0.62352943420410156), (0.91176468133926392, 0.63921570777893066, 0.63921570777893066), (0.91596639156341553, 0.65490198135375977, 0.65490198135375977), (0.92016804218292236, 0.67058825492858887, 0.67058825492858887), (0.92436975240707397, 0.68627452850341797, 0.68627452850341797), (0.92857140302658081, 0.69803923368453979, 0.69803923368453979), (0.93277311325073242, 0.7137255072593689, 0.7137255072593689), (0.93697476387023926, 0.729411780834198, 0.729411780834198), (0.94117647409439087, 0.7450980544090271, 0.7450980544090271), (0.94537812471389771, 0.7607843279838562, 0.7607843279838562), (0.94957983493804932, 0.77254903316497803, 0.77254903316497803), (0.95378148555755615, 0.78823530673980713, 0.78823530673980713), (0.95798319578170776, 0.80392158031463623, 0.80392158031463623), (0.9621848464012146, 0.81960785388946533, 0.81960785388946533), (0.96638655662536621, 0.84705883264541626, 0.84705883264541626), (0.97058820724487305, 0.86274510622024536, 0.86274510622024536), (0.97478991746902466, 0.87843137979507446, 0.87843137979507446), (0.97899156808853149, 0.89411765336990356, 0.89411765336990356), (0.98319327831268311, 0.90980392694473267, 0.90980392694473267), (0.98739492893218994, 0.92156863212585449, 0.92156863212585449), (0.99159663915634155, 0.93725490570068359, 0.93725490570068359), (0.99579828977584839, 0.9529411792755127, 0.9529411792755127), (1.0, 0.9686274528503418, 0.9686274528503418)], ) return ColorMapper.from_segment_map(_data, range=range, **traits) def gist_yarg(range, **traits): """ Generator for the 'gist_yarg' colormap from GIST. """ _data = dict( red = [(0.0, 1.0, 1.0), (0.0042016808874905109, 0.99607843160629272, 0.99607843160629272), (0.0084033617749810219, 0.99215686321258545, 0.99215686321258545), (0.012605042196810246, 0.98823529481887817, 0.98823529481887817), (0.016806723549962044, 0.9843137264251709, 0.9843137264251709), (0.021008403971791267, 0.98039215803146362, 0.98039215803146362), (0.025210084393620491, 0.97647058963775635, 0.97647058963775635), (0.029411764815449715, 0.97254902124404907, 0.97254902124404907), (0.033613447099924088, 0.96470588445663452, 0.96470588445663452), (0.037815127521753311, 0.96078431606292725, 0.96078431606292725), (0.042016807943582535, 0.95686274766921997, 0.95686274766921997), (0.046218488365411758, 0.9529411792755127, 0.9529411792755127), (0.050420168787240982, 0.94901961088180542, 0.94901961088180542), (0.054621849209070206, 0.94509804248809814, 0.94509804248809814), (0.058823529630899429, 0.94117647409439087, 0.94117647409439087), (0.063025213778018951, 0.93725490570068359, 0.93725490570068359), (0.067226894199848175, 0.93333333730697632, 0.93333333730697632), (0.071428574621677399, 0.92941176891326904, 0.92941176891326904), (0.075630255043506622, 0.92549020051956177, 0.92549020051956177), (0.079831935465335846, 0.92156863212585449, 0.92156863212585449), (0.08403361588716507, 0.91764706373214722, 0.91764706373214722), (0.088235296308994293, 0.91372549533843994, 0.91372549533843994), (0.092436976730823517, 0.90980392694473267, 0.90980392694473267), (0.09663865715265274, 0.90196079015731812, 0.90196079015731812), (0.10084033757448196, 0.89803922176361084, 0.89803922176361084), (0.10504201799631119, 0.89411765336990356, 0.89411765336990356), (0.10924369841814041, 0.89019608497619629, 0.89019608497619629), (0.11344537883996964, 0.88627451658248901, 0.88627451658248901), (0.11764705926179886, 0.88235294818878174, 0.88235294818878174), (0.12184873968362808, 0.87843137979507446, 0.87843137979507446), (0.1260504275560379, 0.87450981140136719, 0.87450981140136719), (0.13025210797786713, 0.87058824300765991, 0.87058824300765991), (0.13445378839969635, 0.86666667461395264, 0.86666667461395264), (0.13865546882152557, 0.86274510622024536, 0.86274510622024536), (0.1428571492433548, 0.85882353782653809, 0.85882353782653809), (0.14705882966518402, 0.85490196943283081, 0.85490196943283081), (0.15126051008701324, 0.85098040103912354, 0.85098040103912354), (0.15546219050884247, 0.84705883264541626, 0.84705883264541626), (0.15966387093067169, 0.83921569585800171, 0.83921569585800171), (0.16386555135250092, 0.83529412746429443, 0.83529412746429443), (0.16806723177433014, 0.83137255907058716, 0.83137255907058716), (0.17226891219615936, 0.82745099067687988, 0.82745099067687988), (0.17647059261798859, 0.82352942228317261, 0.82352942228317261), (0.18067227303981781, 0.81960785388946533, 0.81960785388946533), (0.18487395346164703, 0.81568628549575806, 0.81568628549575806), (0.18907563388347626, 0.81176471710205078, 0.81176471710205078), (0.19327731430530548, 0.80784314870834351, 0.80784314870834351), (0.1974789947271347, 0.80392158031463623, 0.80392158031463623), (0.20168067514896393, 0.80000001192092896, 0.80000001192092896), (0.20588235557079315, 0.79607844352722168, 0.79607844352722168), (0.21008403599262238, 0.7921568751335144, 0.7921568751335144), (0.2142857164144516, 0.78823530673980713, 0.78823530673980713), (0.21848739683628082, 0.78431373834609985, 0.78431373834609985), (0.22268907725811005, 0.7764706015586853, 0.7764706015586853), (0.22689075767993927, 0.77254903316497803, 0.77254903316497803), (0.23109243810176849, 0.76862746477127075, 0.76862746477127075), (0.23529411852359772, 0.76470589637756348, 0.76470589637756348), (0.23949579894542694, 0.7607843279838562, 0.7607843279838562), (0.24369747936725616, 0.75686275959014893, 0.75686275959014893), (0.24789915978908539, 0.75294119119644165, 0.75294119119644165), (0.25210085511207581, 0.74901962280273438, 0.74901962280273438), (0.25630253553390503, 0.7450980544090271, 0.7450980544090271), (0.26050421595573425, 0.74117648601531982, 0.74117648601531982), (0.26470589637756348, 0.73725491762161255, 0.73725491762161255), (0.2689075767993927, 0.73333334922790527, 0.73333334922790527), (0.27310925722122192, 0.729411780834198, 0.729411780834198), (0.27731093764305115, 0.72549021244049072, 0.72549021244049072), (0.28151261806488037, 0.72156864404678345, 0.72156864404678345), (0.28571429848670959, 0.7137255072593689, 0.7137255072593689), (0.28991597890853882, 0.70980393886566162, 0.70980393886566162), (0.29411765933036804, 0.70588237047195435, 0.70588237047195435), (0.29831933975219727, 0.70196080207824707, 0.70196080207824707), (0.30252102017402649, 0.69803923368453979, 0.69803923368453979), (0.30672270059585571, 0.69411766529083252, 0.69411766529083252), (0.31092438101768494, 0.69019609689712524, 0.69019609689712524), (0.31512606143951416, 0.68627452850341797, 0.68627452850341797), (0.31932774186134338, 0.68235296010971069, 0.68235296010971069), (0.32352942228317261, 0.67843139171600342, 0.67843139171600342), (0.32773110270500183, 0.67450982332229614, 0.67450982332229614), (0.33193278312683105, 0.67058825492858887, 0.67058825492858887), (0.33613446354866028, 0.66666668653488159, 0.66666668653488159), (0.3403361439704895, 0.66274511814117432, 0.66274511814117432), (0.34453782439231873, 0.65882354974746704, 0.65882354974746704), (0.34873950481414795, 0.65098041296005249, 0.65098041296005249), (0.35294118523597717, 0.64705884456634521, 0.64705884456634521), (0.3571428656578064, 0.64313727617263794, 0.64313727617263794), (0.36134454607963562, 0.63921570777893066, 0.63921570777893066), (0.36554622650146484, 0.63529413938522339, 0.63529413938522339), (0.36974790692329407, 0.63137257099151611, 0.63137257099151611), (0.37394958734512329, 0.62745100259780884, 0.62745100259780884), (0.37815126776695251, 0.62352943420410156, 0.62352943420410156), (0.38235294818878174, 0.61960786581039429, 0.61960786581039429), (0.38655462861061096, 0.61568629741668701, 0.61568629741668701), (0.39075630903244019, 0.61176472902297974, 0.61176472902297974), (0.39495798945426941, 0.60784316062927246, 0.60784316062927246), (0.39915966987609863, 0.60392159223556519, 0.60392159223556519), (0.40336135029792786, 0.60000002384185791, 0.60000002384185791), (0.40756303071975708, 0.59607845544815063, 0.59607845544815063), (0.4117647111415863, 0.58823531866073608, 0.58823531866073608), (0.41596639156341553, 0.58431375026702881, 0.58431375026702881), (0.42016807198524475, 0.58039218187332153, 0.58039218187332153), (0.42436975240707397, 0.57647061347961426, 0.57647061347961426), (0.4285714328289032, 0.57254904508590698, 0.57254904508590698), (0.43277311325073242, 0.56862747669219971, 0.56862747669219971), (0.43697479367256165, 0.56470590829849243, 0.56470590829849243), (0.44117647409439087, 0.56078433990478516, 0.56078433990478516), (0.44537815451622009, 0.55686277151107788, 0.55686277151107788), (0.44957983493804932, 0.55294120311737061, 0.55294120311737061), (0.45378151535987854, 0.54901963472366333, 0.54901963472366333), (0.45798319578170776, 0.54509806632995605, 0.54509806632995605), (0.46218487620353699, 0.54117649793624878, 0.54117649793624878), (0.46638655662536621, 0.5372549295425415, 0.5372549295425415), (0.47058823704719543, 0.53333336114883423, 0.53333336114883423), (0.47478991746902466, 0.52549022436141968, 0.52549022436141968), (0.47899159789085388, 0.5215686559677124, 0.5215686559677124), (0.48319327831268311, 0.51764708757400513, 0.51764708757400513), (0.48739495873451233, 0.51372551918029785, 0.51372551918029785), (0.49159663915634155, 0.50980395078659058, 0.50980395078659058), (0.49579831957817078, 0.5058823823928833, 0.5058823823928833), (0.5, 0.50196081399917603, 0.50196081399917603), (0.50420171022415161, 0.49803921580314636, 0.49803921580314636), (0.50840336084365845, 0.49411764740943909, 0.49411764740943909), (0.51260507106781006, 0.49019607901573181, 0.49019607901573181), (0.51680672168731689, 0.48627451062202454, 0.48627451062202454), (0.52100843191146851, 0.48235294222831726, 0.48235294222831726), (0.52521008253097534, 0.47843137383460999, 0.47843137383460999), (0.52941179275512695, 0.47450980544090271, 0.47450980544090271), (0.53361344337463379, 0.47058823704719543, 0.47058823704719543), (0.5378151535987854, 0.46274510025978088, 0.46274510025978088), (0.54201680421829224, 0.45882353186607361, 0.45882353186607361), (0.54621851444244385, 0.45490196347236633, 0.45490196347236633), (0.55042016506195068, 0.45098039507865906, 0.45098039507865906), (0.55462187528610229, 0.44705882668495178, 0.44705882668495178), (0.55882352590560913, 0.44313725829124451, 0.44313725829124451), (0.56302523612976074, 0.43921568989753723, 0.43921568989753723), (0.56722688674926758, 0.43529412150382996, 0.43529412150382996), (0.57142859697341919, 0.43137255311012268, 0.43137255311012268), (0.57563024759292603, 0.42745098471641541, 0.42745098471641541), (0.57983195781707764, 0.42352941632270813, 0.42352941632270813), (0.58403360843658447, 0.41960784792900085, 0.41960784792900085), (0.58823531866073608, 0.41568627953529358, 0.41568627953529358), (0.59243696928024292, 0.4117647111415863, 0.4117647111415863), (0.59663867950439453, 0.40784314274787903, 0.40784314274787903), (0.60084033012390137, 0.40000000596046448, 0.40000000596046448), (0.60504204034805298, 0.3960784375667572, 0.3960784375667572), (0.60924369096755981, 0.39215686917304993, 0.39215686917304993), (0.61344540119171143, 0.38823530077934265, 0.38823530077934265), (0.61764705181121826, 0.38431373238563538, 0.38431373238563538), (0.62184876203536987, 0.3803921639919281, 0.3803921639919281), (0.62605041265487671, 0.37647059559822083, 0.37647059559822083), (0.63025212287902832, 0.37254902720451355, 0.37254902720451355), (0.63445377349853516, 0.36862745881080627, 0.36862745881080627), (0.63865548372268677, 0.364705890417099, 0.364705890417099), (0.6428571343421936, 0.36078432202339172, 0.36078432202339172), (0.64705884456634521, 0.35686275362968445, 0.35686275362968445), (0.65126049518585205, 0.35294118523597717, 0.35294118523597717), (0.65546220541000366, 0.3490196168422699, 0.3490196168422699), (0.6596638560295105, 0.34509804844856262, 0.34509804844856262), (0.66386556625366211, 0.33725491166114807, 0.33725491166114807), (0.66806721687316895, 0.3333333432674408, 0.3333333432674408), (0.67226892709732056, 0.32941177487373352, 0.32941177487373352), (0.67647057771682739, 0.32549020648002625, 0.32549020648002625), (0.680672287940979, 0.32156863808631897, 0.32156863808631897), (0.68487393856048584, 0.31764706969261169, 0.31764706969261169), (0.68907564878463745, 0.31372550129890442, 0.31372550129890442), (0.69327729940414429, 0.30980393290519714, 0.30980393290519714), (0.6974790096282959, 0.30588236451148987, 0.30588236451148987), (0.70168066024780273, 0.30196079611778259, 0.30196079611778259), (0.70588237047195435, 0.29803922772407532, 0.29803922772407532), (0.71008402109146118, 0.29411765933036804, 0.29411765933036804), (0.71428573131561279, 0.29019609093666077, 0.29019609093666077), (0.71848738193511963, 0.28627452254295349, 0.28627452254295349), (0.72268909215927124, 0.28235295414924622, 0.28235295414924622), (0.72689074277877808, 0.27450981736183167, 0.27450981736183167), (0.73109245300292969, 0.27058824896812439, 0.27058824896812439), (0.73529410362243652, 0.26666668057441711, 0.26666668057441711), (0.73949581384658813, 0.26274511218070984, 0.26274511218070984), (0.74369746446609497, 0.25882354378700256, 0.25882354378700256), (0.74789917469024658, 0.25490197539329529, 0.25490197539329529), (0.75210082530975342, 0.25098040699958801, 0.25098040699958801), (0.75630253553390503, 0.24705882370471954, 0.24705882370471954), (0.76050418615341187, 0.24313725531101227, 0.24313725531101227), (0.76470589637756348, 0.23921568691730499, 0.23921568691730499), (0.76890754699707031, 0.23529411852359772, 0.23529411852359772), (0.77310925722122192, 0.23137255012989044, 0.23137255012989044), (0.77731090784072876, 0.22745098173618317, 0.22745098173618317), (0.78151261806488037, 0.22352941334247589, 0.22352941334247589), (0.78571426868438721, 0.21960784494876862, 0.21960784494876862), (0.78991597890853882, 0.21176470816135406, 0.21176470816135406), (0.79411762952804565, 0.20784313976764679, 0.20784313976764679), (0.79831933975219727, 0.20392157137393951, 0.20392157137393951), (0.8025209903717041, 0.20000000298023224, 0.20000000298023224), (0.80672270059585571, 0.19607843458652496, 0.19607843458652496), (0.81092435121536255, 0.19215686619281769, 0.19215686619281769), (0.81512606143951416, 0.18823529779911041, 0.18823529779911041), (0.819327712059021, 0.18431372940540314, 0.18431372940540314), (0.82352942228317261, 0.18039216101169586, 0.18039216101169586), (0.82773107290267944, 0.17647059261798859, 0.17647059261798859), (0.83193278312683105, 0.17254902422428131, 0.17254902422428131), (0.83613443374633789, 0.16862745583057404, 0.16862745583057404), (0.8403361439704895, 0.16470588743686676, 0.16470588743686676), (0.84453779458999634, 0.16078431904315948, 0.16078431904315948), (0.84873950481414795, 0.15686275064945221, 0.15686275064945221), (0.85294115543365479, 0.14901961386203766, 0.14901961386203766), (0.8571428656578064, 0.14509804546833038, 0.14509804546833038), (0.86134451627731323, 0.14117647707462311, 0.14117647707462311), (0.86554622650146484, 0.13725490868091583, 0.13725490868091583), (0.86974787712097168, 0.13333334028720856, 0.13333334028720856), (0.87394958734512329, 0.12941177189350128, 0.12941177189350128), (0.87815123796463013, 0.12549020349979401, 0.12549020349979401), (0.88235294818878174, 0.12156862765550613, 0.12156862765550613), (0.88655459880828857, 0.11764705926179886, 0.11764705926179886), (0.89075630903244019, 0.11372549086809158, 0.11372549086809158), (0.89495795965194702, 0.10980392247438431, 0.10980392247438431), (0.89915966987609863, 0.10588235408067703, 0.10588235408067703), (0.90336132049560547, 0.10196078568696976, 0.10196078568696976), (0.90756303071975708, 0.098039217293262482, 0.098039217293262482), (0.91176468133926392, 0.094117648899555206, 0.094117648899555206), (0.91596639156341553, 0.086274512112140656, 0.086274512112140656), (0.92016804218292236, 0.08235294371843338, 0.08235294371843338), (0.92436975240707397, 0.078431375324726105, 0.078431375324726105), (0.92857140302658081, 0.074509806931018829, 0.074509806931018829), (0.93277311325073242, 0.070588238537311554, 0.070588238537311554), (0.93697476387023926, 0.066666670143604279, 0.066666670143604279), (0.94117647409439087, 0.062745101749897003, 0.062745101749897003), (0.94537812471389771, 0.058823529630899429, 0.058823529630899429), (0.94957983493804932, 0.054901961237192154, 0.054901961237192154), (0.95378148555755615, 0.050980392843484879, 0.050980392843484879), (0.95798319578170776, 0.047058824449777603, 0.047058824449777603), (0.9621848464012146, 0.043137256056070328, 0.043137256056070328), (0.96638655662536621, 0.039215687662363052, 0.039215687662363052), (0.97058820724487305, 0.035294119268655777, 0.035294119268655777), (0.97478991746902466, 0.031372550874948502, 0.031372550874948502), (0.97899156808853149, 0.023529412224888802, 0.023529412224888802), (0.98319327831268311, 0.019607843831181526, 0.019607843831181526), (0.98739492893218994, 0.015686275437474251, 0.015686275437474251), (0.99159663915634155, 0.011764706112444401, 0.011764706112444401), (0.99579828977584839, 0.0078431377187371254, 0.0078431377187371254), (1.0, 0.0039215688593685627, 0.0039215688593685627)], green = [(0.0, 1.0, 1.0), (0.0042016808874905109, 0.99607843160629272, 0.99607843160629272), (0.0084033617749810219, 0.99215686321258545, 0.99215686321258545), (0.012605042196810246, 0.98823529481887817, 0.98823529481887817), (0.016806723549962044, 0.9843137264251709, 0.9843137264251709), (0.021008403971791267, 0.98039215803146362, 0.98039215803146362), (0.025210084393620491, 0.97647058963775635, 0.97647058963775635), (0.029411764815449715, 0.97254902124404907, 0.97254902124404907), (0.033613447099924088, 0.96470588445663452, 0.96470588445663452), (0.037815127521753311, 0.96078431606292725, 0.96078431606292725), (0.042016807943582535, 0.95686274766921997, 0.95686274766921997), (0.046218488365411758, 0.9529411792755127, 0.9529411792755127), (0.050420168787240982, 0.94901961088180542, 0.94901961088180542), (0.054621849209070206, 0.94509804248809814, 0.94509804248809814), (0.058823529630899429, 0.94117647409439087, 0.94117647409439087), (0.063025213778018951, 0.93725490570068359, 0.93725490570068359), (0.067226894199848175, 0.93333333730697632, 0.93333333730697632), (0.071428574621677399, 0.92941176891326904, 0.92941176891326904), (0.075630255043506622, 0.92549020051956177, 0.92549020051956177), (0.079831935465335846, 0.92156863212585449, 0.92156863212585449), (0.08403361588716507, 0.91764706373214722, 0.91764706373214722), (0.088235296308994293, 0.91372549533843994, 0.91372549533843994), (0.092436976730823517, 0.90980392694473267, 0.90980392694473267), (0.09663865715265274, 0.90196079015731812, 0.90196079015731812), (0.10084033757448196, 0.89803922176361084, 0.89803922176361084), (0.10504201799631119, 0.89411765336990356, 0.89411765336990356), (0.10924369841814041, 0.89019608497619629, 0.89019608497619629), (0.11344537883996964, 0.88627451658248901, 0.88627451658248901), (0.11764705926179886, 0.88235294818878174, 0.88235294818878174), (0.12184873968362808, 0.87843137979507446, 0.87843137979507446), (0.1260504275560379, 0.87450981140136719, 0.87450981140136719), (0.13025210797786713, 0.87058824300765991, 0.87058824300765991), (0.13445378839969635, 0.86666667461395264, 0.86666667461395264), (0.13865546882152557, 0.86274510622024536, 0.86274510622024536), (0.1428571492433548, 0.85882353782653809, 0.85882353782653809), (0.14705882966518402, 0.85490196943283081, 0.85490196943283081), (0.15126051008701324, 0.85098040103912354, 0.85098040103912354), (0.15546219050884247, 0.84705883264541626, 0.84705883264541626), (0.15966387093067169, 0.83921569585800171, 0.83921569585800171), (0.16386555135250092, 0.83529412746429443, 0.83529412746429443), (0.16806723177433014, 0.83137255907058716, 0.83137255907058716), (0.17226891219615936, 0.82745099067687988, 0.82745099067687988), (0.17647059261798859, 0.82352942228317261, 0.82352942228317261), (0.18067227303981781, 0.81960785388946533, 0.81960785388946533), (0.18487395346164703, 0.81568628549575806, 0.81568628549575806), (0.18907563388347626, 0.81176471710205078, 0.81176471710205078), (0.19327731430530548, 0.80784314870834351, 0.80784314870834351), (0.1974789947271347, 0.80392158031463623, 0.80392158031463623), (0.20168067514896393, 0.80000001192092896, 0.80000001192092896), (0.20588235557079315, 0.79607844352722168, 0.79607844352722168), (0.21008403599262238, 0.7921568751335144, 0.7921568751335144), (0.2142857164144516, 0.78823530673980713, 0.78823530673980713), (0.21848739683628082, 0.78431373834609985, 0.78431373834609985), (0.22268907725811005, 0.7764706015586853, 0.7764706015586853), (0.22689075767993927, 0.77254903316497803, 0.77254903316497803), (0.23109243810176849, 0.76862746477127075, 0.76862746477127075), (0.23529411852359772, 0.76470589637756348, 0.76470589637756348), (0.23949579894542694, 0.7607843279838562, 0.7607843279838562), (0.24369747936725616, 0.75686275959014893, 0.75686275959014893), (0.24789915978908539, 0.75294119119644165, 0.75294119119644165), (0.25210085511207581, 0.74901962280273438, 0.74901962280273438), (0.25630253553390503, 0.7450980544090271, 0.7450980544090271), (0.26050421595573425, 0.74117648601531982, 0.74117648601531982), (0.26470589637756348, 0.73725491762161255, 0.73725491762161255), (0.2689075767993927, 0.73333334922790527, 0.73333334922790527), (0.27310925722122192, 0.729411780834198, 0.729411780834198), (0.27731093764305115, 0.72549021244049072, 0.72549021244049072), (0.28151261806488037, 0.72156864404678345, 0.72156864404678345), (0.28571429848670959, 0.7137255072593689, 0.7137255072593689), (0.28991597890853882, 0.70980393886566162, 0.70980393886566162), (0.29411765933036804, 0.70588237047195435, 0.70588237047195435), (0.29831933975219727, 0.70196080207824707, 0.70196080207824707), (0.30252102017402649, 0.69803923368453979, 0.69803923368453979), (0.30672270059585571, 0.69411766529083252, 0.69411766529083252), (0.31092438101768494, 0.69019609689712524, 0.69019609689712524), (0.31512606143951416, 0.68627452850341797, 0.68627452850341797), (0.31932774186134338, 0.68235296010971069, 0.68235296010971069), (0.32352942228317261, 0.67843139171600342, 0.67843139171600342), (0.32773110270500183, 0.67450982332229614, 0.67450982332229614), (0.33193278312683105, 0.67058825492858887, 0.67058825492858887), (0.33613446354866028, 0.66666668653488159, 0.66666668653488159), (0.3403361439704895, 0.66274511814117432, 0.66274511814117432), (0.34453782439231873, 0.65882354974746704, 0.65882354974746704), (0.34873950481414795, 0.65098041296005249, 0.65098041296005249), (0.35294118523597717, 0.64705884456634521, 0.64705884456634521), (0.3571428656578064, 0.64313727617263794, 0.64313727617263794), (0.36134454607963562, 0.63921570777893066, 0.63921570777893066), (0.36554622650146484, 0.63529413938522339, 0.63529413938522339), (0.36974790692329407, 0.63137257099151611, 0.63137257099151611), (0.37394958734512329, 0.62745100259780884, 0.62745100259780884), (0.37815126776695251, 0.62352943420410156, 0.62352943420410156), (0.38235294818878174, 0.61960786581039429, 0.61960786581039429), (0.38655462861061096, 0.61568629741668701, 0.61568629741668701), (0.39075630903244019, 0.61176472902297974, 0.61176472902297974), (0.39495798945426941, 0.60784316062927246, 0.60784316062927246), (0.39915966987609863, 0.60392159223556519, 0.60392159223556519), (0.40336135029792786, 0.60000002384185791, 0.60000002384185791), (0.40756303071975708, 0.59607845544815063, 0.59607845544815063), (0.4117647111415863, 0.58823531866073608, 0.58823531866073608), (0.41596639156341553, 0.58431375026702881, 0.58431375026702881), (0.42016807198524475, 0.58039218187332153, 0.58039218187332153), (0.42436975240707397, 0.57647061347961426, 0.57647061347961426), (0.4285714328289032, 0.57254904508590698, 0.57254904508590698), (0.43277311325073242, 0.56862747669219971, 0.56862747669219971), (0.43697479367256165, 0.56470590829849243, 0.56470590829849243), (0.44117647409439087, 0.56078433990478516, 0.56078433990478516), (0.44537815451622009, 0.55686277151107788, 0.55686277151107788), (0.44957983493804932, 0.55294120311737061, 0.55294120311737061), (0.45378151535987854, 0.54901963472366333, 0.54901963472366333), (0.45798319578170776, 0.54509806632995605, 0.54509806632995605), (0.46218487620353699, 0.54117649793624878, 0.54117649793624878), (0.46638655662536621, 0.5372549295425415, 0.5372549295425415), (0.47058823704719543, 0.53333336114883423, 0.53333336114883423), (0.47478991746902466, 0.52549022436141968, 0.52549022436141968), (0.47899159789085388, 0.5215686559677124, 0.5215686559677124), (0.48319327831268311, 0.51764708757400513, 0.51764708757400513), (0.48739495873451233, 0.51372551918029785, 0.51372551918029785), (0.49159663915634155, 0.50980395078659058, 0.50980395078659058), (0.49579831957817078, 0.5058823823928833, 0.5058823823928833), (0.5, 0.50196081399917603, 0.50196081399917603), (0.50420171022415161, 0.49803921580314636, 0.49803921580314636), (0.50840336084365845, 0.49411764740943909, 0.49411764740943909), (0.51260507106781006, 0.49019607901573181, 0.49019607901573181), (0.51680672168731689, 0.48627451062202454, 0.48627451062202454), (0.52100843191146851, 0.48235294222831726, 0.48235294222831726), (0.52521008253097534, 0.47843137383460999, 0.47843137383460999), (0.52941179275512695, 0.47450980544090271, 0.47450980544090271), (0.53361344337463379, 0.47058823704719543, 0.47058823704719543), (0.5378151535987854, 0.46274510025978088, 0.46274510025978088), (0.54201680421829224, 0.45882353186607361, 0.45882353186607361), (0.54621851444244385, 0.45490196347236633, 0.45490196347236633), (0.55042016506195068, 0.45098039507865906, 0.45098039507865906), (0.55462187528610229, 0.44705882668495178, 0.44705882668495178), (0.55882352590560913, 0.44313725829124451, 0.44313725829124451), (0.56302523612976074, 0.43921568989753723, 0.43921568989753723), (0.56722688674926758, 0.43529412150382996, 0.43529412150382996), (0.57142859697341919, 0.43137255311012268, 0.43137255311012268), (0.57563024759292603, 0.42745098471641541, 0.42745098471641541), (0.57983195781707764, 0.42352941632270813, 0.42352941632270813), (0.58403360843658447, 0.41960784792900085, 0.41960784792900085), (0.58823531866073608, 0.41568627953529358, 0.41568627953529358), (0.59243696928024292, 0.4117647111415863, 0.4117647111415863), (0.59663867950439453, 0.40784314274787903, 0.40784314274787903), (0.60084033012390137, 0.40000000596046448, 0.40000000596046448), (0.60504204034805298, 0.3960784375667572, 0.3960784375667572), (0.60924369096755981, 0.39215686917304993, 0.39215686917304993), (0.61344540119171143, 0.38823530077934265, 0.38823530077934265), (0.61764705181121826, 0.38431373238563538, 0.38431373238563538), (0.62184876203536987, 0.3803921639919281, 0.3803921639919281), (0.62605041265487671, 0.37647059559822083, 0.37647059559822083), (0.63025212287902832, 0.37254902720451355, 0.37254902720451355), (0.63445377349853516, 0.36862745881080627, 0.36862745881080627), (0.63865548372268677, 0.364705890417099, 0.364705890417099), (0.6428571343421936, 0.36078432202339172, 0.36078432202339172), (0.64705884456634521, 0.35686275362968445, 0.35686275362968445), (0.65126049518585205, 0.35294118523597717, 0.35294118523597717), (0.65546220541000366, 0.3490196168422699, 0.3490196168422699), (0.6596638560295105, 0.34509804844856262, 0.34509804844856262), (0.66386556625366211, 0.33725491166114807, 0.33725491166114807), (0.66806721687316895, 0.3333333432674408, 0.3333333432674408), (0.67226892709732056, 0.32941177487373352, 0.32941177487373352), (0.67647057771682739, 0.32549020648002625, 0.32549020648002625), (0.680672287940979, 0.32156863808631897, 0.32156863808631897), (0.68487393856048584, 0.31764706969261169, 0.31764706969261169), (0.68907564878463745, 0.31372550129890442, 0.31372550129890442), (0.69327729940414429, 0.30980393290519714, 0.30980393290519714), (0.6974790096282959, 0.30588236451148987, 0.30588236451148987), (0.70168066024780273, 0.30196079611778259, 0.30196079611778259), (0.70588237047195435, 0.29803922772407532, 0.29803922772407532), (0.71008402109146118, 0.29411765933036804, 0.29411765933036804), (0.71428573131561279, 0.29019609093666077, 0.29019609093666077), (0.71848738193511963, 0.28627452254295349, 0.28627452254295349), (0.72268909215927124, 0.28235295414924622, 0.28235295414924622), (0.72689074277877808, 0.27450981736183167, 0.27450981736183167), (0.73109245300292969, 0.27058824896812439, 0.27058824896812439), (0.73529410362243652, 0.26666668057441711, 0.26666668057441711), (0.73949581384658813, 0.26274511218070984, 0.26274511218070984), (0.74369746446609497, 0.25882354378700256, 0.25882354378700256), (0.74789917469024658, 0.25490197539329529, 0.25490197539329529), (0.75210082530975342, 0.25098040699958801, 0.25098040699958801), (0.75630253553390503, 0.24705882370471954, 0.24705882370471954), (0.76050418615341187, 0.24313725531101227, 0.24313725531101227), (0.76470589637756348, 0.23921568691730499, 0.23921568691730499), (0.76890754699707031, 0.23529411852359772, 0.23529411852359772), (0.77310925722122192, 0.23137255012989044, 0.23137255012989044), (0.77731090784072876, 0.22745098173618317, 0.22745098173618317), (0.78151261806488037, 0.22352941334247589, 0.22352941334247589), (0.78571426868438721, 0.21960784494876862, 0.21960784494876862), (0.78991597890853882, 0.21176470816135406, 0.21176470816135406), (0.79411762952804565, 0.20784313976764679, 0.20784313976764679), (0.79831933975219727, 0.20392157137393951, 0.20392157137393951), (0.8025209903717041, 0.20000000298023224, 0.20000000298023224), (0.80672270059585571, 0.19607843458652496, 0.19607843458652496), (0.81092435121536255, 0.19215686619281769, 0.19215686619281769), (0.81512606143951416, 0.18823529779911041, 0.18823529779911041), (0.819327712059021, 0.18431372940540314, 0.18431372940540314), (0.82352942228317261, 0.18039216101169586, 0.18039216101169586), (0.82773107290267944, 0.17647059261798859, 0.17647059261798859), (0.83193278312683105, 0.17254902422428131, 0.17254902422428131), (0.83613443374633789, 0.16862745583057404, 0.16862745583057404), (0.8403361439704895, 0.16470588743686676, 0.16470588743686676), (0.84453779458999634, 0.16078431904315948, 0.16078431904315948), (0.84873950481414795, 0.15686275064945221, 0.15686275064945221), (0.85294115543365479, 0.14901961386203766, 0.14901961386203766), (0.8571428656578064, 0.14509804546833038, 0.14509804546833038), (0.86134451627731323, 0.14117647707462311, 0.14117647707462311), (0.86554622650146484, 0.13725490868091583, 0.13725490868091583), (0.86974787712097168, 0.13333334028720856, 0.13333334028720856), (0.87394958734512329, 0.12941177189350128, 0.12941177189350128), (0.87815123796463013, 0.12549020349979401, 0.12549020349979401), (0.88235294818878174, 0.12156862765550613, 0.12156862765550613), (0.88655459880828857, 0.11764705926179886, 0.11764705926179886), (0.89075630903244019, 0.11372549086809158, 0.11372549086809158), (0.89495795965194702, 0.10980392247438431, 0.10980392247438431), (0.89915966987609863, 0.10588235408067703, 0.10588235408067703), (0.90336132049560547, 0.10196078568696976, 0.10196078568696976), (0.90756303071975708, 0.098039217293262482, 0.098039217293262482), (0.91176468133926392, 0.094117648899555206, 0.094117648899555206), (0.91596639156341553, 0.086274512112140656, 0.086274512112140656), (0.92016804218292236, 0.08235294371843338, 0.08235294371843338), (0.92436975240707397, 0.078431375324726105, 0.078431375324726105), (0.92857140302658081, 0.074509806931018829, 0.074509806931018829), (0.93277311325073242, 0.070588238537311554, 0.070588238537311554), (0.93697476387023926, 0.066666670143604279, 0.066666670143604279), (0.94117647409439087, 0.062745101749897003, 0.062745101749897003), (0.94537812471389771, 0.058823529630899429, 0.058823529630899429), (0.94957983493804932, 0.054901961237192154, 0.054901961237192154), (0.95378148555755615, 0.050980392843484879, 0.050980392843484879), (0.95798319578170776, 0.047058824449777603, 0.047058824449777603), (0.9621848464012146, 0.043137256056070328, 0.043137256056070328), (0.96638655662536621, 0.039215687662363052, 0.039215687662363052), (0.97058820724487305, 0.035294119268655777, 0.035294119268655777), (0.97478991746902466, 0.031372550874948502, 0.031372550874948502), (0.97899156808853149, 0.023529412224888802, 0.023529412224888802), (0.98319327831268311, 0.019607843831181526, 0.019607843831181526), (0.98739492893218994, 0.015686275437474251, 0.015686275437474251), (0.99159663915634155, 0.011764706112444401, 0.011764706112444401), (0.99579828977584839, 0.0078431377187371254, 0.0078431377187371254), (1.0, 0.0039215688593685627, 0.0039215688593685627)], blue = [(0.0, 1.0, 1.0), (0.0042016808874905109, 0.99607843160629272, 0.99607843160629272), (0.0084033617749810219, 0.99215686321258545, 0.99215686321258545), (0.012605042196810246, 0.98823529481887817, 0.98823529481887817), (0.016806723549962044, 0.9843137264251709, 0.9843137264251709), (0.021008403971791267, 0.98039215803146362, 0.98039215803146362), (0.025210084393620491, 0.97647058963775635, 0.97647058963775635), (0.029411764815449715, 0.97254902124404907, 0.97254902124404907), (0.033613447099924088, 0.96470588445663452, 0.96470588445663452), (0.037815127521753311, 0.96078431606292725, 0.96078431606292725), (0.042016807943582535, 0.95686274766921997, 0.95686274766921997), (0.046218488365411758, 0.9529411792755127, 0.9529411792755127), (0.050420168787240982, 0.94901961088180542, 0.94901961088180542), (0.054621849209070206, 0.94509804248809814, 0.94509804248809814), (0.058823529630899429, 0.94117647409439087, 0.94117647409439087), (0.063025213778018951, 0.93725490570068359, 0.93725490570068359), (0.067226894199848175, 0.93333333730697632, 0.93333333730697632), (0.071428574621677399, 0.92941176891326904, 0.92941176891326904), (0.075630255043506622, 0.92549020051956177, 0.92549020051956177), (0.079831935465335846, 0.92156863212585449, 0.92156863212585449), (0.08403361588716507, 0.91764706373214722, 0.91764706373214722), (0.088235296308994293, 0.91372549533843994, 0.91372549533843994), (0.092436976730823517, 0.90980392694473267, 0.90980392694473267), (0.09663865715265274, 0.90196079015731812, 0.90196079015731812), (0.10084033757448196, 0.89803922176361084, 0.89803922176361084), (0.10504201799631119, 0.89411765336990356, 0.89411765336990356), (0.10924369841814041, 0.89019608497619629, 0.89019608497619629), (0.11344537883996964, 0.88627451658248901, 0.88627451658248901), (0.11764705926179886, 0.88235294818878174, 0.88235294818878174), (0.12184873968362808, 0.87843137979507446, 0.87843137979507446), (0.1260504275560379, 0.87450981140136719, 0.87450981140136719), (0.13025210797786713, 0.87058824300765991, 0.87058824300765991), (0.13445378839969635, 0.86666667461395264, 0.86666667461395264), (0.13865546882152557, 0.86274510622024536, 0.86274510622024536), (0.1428571492433548, 0.85882353782653809, 0.85882353782653809), (0.14705882966518402, 0.85490196943283081, 0.85490196943283081), (0.15126051008701324, 0.85098040103912354, 0.85098040103912354), (0.15546219050884247, 0.84705883264541626, 0.84705883264541626), (0.15966387093067169, 0.83921569585800171, 0.83921569585800171), (0.16386555135250092, 0.83529412746429443, 0.83529412746429443), (0.16806723177433014, 0.83137255907058716, 0.83137255907058716), (0.17226891219615936, 0.82745099067687988, 0.82745099067687988), (0.17647059261798859, 0.82352942228317261, 0.82352942228317261), (0.18067227303981781, 0.81960785388946533, 0.81960785388946533), (0.18487395346164703, 0.81568628549575806, 0.81568628549575806), (0.18907563388347626, 0.81176471710205078, 0.81176471710205078), (0.19327731430530548, 0.80784314870834351, 0.80784314870834351), (0.1974789947271347, 0.80392158031463623, 0.80392158031463623), (0.20168067514896393, 0.80000001192092896, 0.80000001192092896), (0.20588235557079315, 0.79607844352722168, 0.79607844352722168), (0.21008403599262238, 0.7921568751335144, 0.7921568751335144), (0.2142857164144516, 0.78823530673980713, 0.78823530673980713), (0.21848739683628082, 0.78431373834609985, 0.78431373834609985), (0.22268907725811005, 0.7764706015586853, 0.7764706015586853), (0.22689075767993927, 0.77254903316497803, 0.77254903316497803), (0.23109243810176849, 0.76862746477127075, 0.76862746477127075), (0.23529411852359772, 0.76470589637756348, 0.76470589637756348), (0.23949579894542694, 0.7607843279838562, 0.7607843279838562), (0.24369747936725616, 0.75686275959014893, 0.75686275959014893), (0.24789915978908539, 0.75294119119644165, 0.75294119119644165), (0.25210085511207581, 0.74901962280273438, 0.74901962280273438), (0.25630253553390503, 0.7450980544090271, 0.7450980544090271), (0.26050421595573425, 0.74117648601531982, 0.74117648601531982), (0.26470589637756348, 0.73725491762161255, 0.73725491762161255), (0.2689075767993927, 0.73333334922790527, 0.73333334922790527), (0.27310925722122192, 0.729411780834198, 0.729411780834198), (0.27731093764305115, 0.72549021244049072, 0.72549021244049072), (0.28151261806488037, 0.72156864404678345, 0.72156864404678345), (0.28571429848670959, 0.7137255072593689, 0.7137255072593689), (0.28991597890853882, 0.70980393886566162, 0.70980393886566162), (0.29411765933036804, 0.70588237047195435, 0.70588237047195435), (0.29831933975219727, 0.70196080207824707, 0.70196080207824707), (0.30252102017402649, 0.69803923368453979, 0.69803923368453979), (0.30672270059585571, 0.69411766529083252, 0.69411766529083252), (0.31092438101768494, 0.69019609689712524, 0.69019609689712524), (0.31512606143951416, 0.68627452850341797, 0.68627452850341797), (0.31932774186134338, 0.68235296010971069, 0.68235296010971069), (0.32352942228317261, 0.67843139171600342, 0.67843139171600342), (0.32773110270500183, 0.67450982332229614, 0.67450982332229614), (0.33193278312683105, 0.67058825492858887, 0.67058825492858887), (0.33613446354866028, 0.66666668653488159, 0.66666668653488159), (0.3403361439704895, 0.66274511814117432, 0.66274511814117432), (0.34453782439231873, 0.65882354974746704, 0.65882354974746704), (0.34873950481414795, 0.65098041296005249, 0.65098041296005249), (0.35294118523597717, 0.64705884456634521, 0.64705884456634521), (0.3571428656578064, 0.64313727617263794, 0.64313727617263794), (0.36134454607963562, 0.63921570777893066, 0.63921570777893066), (0.36554622650146484, 0.63529413938522339, 0.63529413938522339), (0.36974790692329407, 0.63137257099151611, 0.63137257099151611), (0.37394958734512329, 0.62745100259780884, 0.62745100259780884), (0.37815126776695251, 0.62352943420410156, 0.62352943420410156), (0.38235294818878174, 0.61960786581039429, 0.61960786581039429), (0.38655462861061096, 0.61568629741668701, 0.61568629741668701), (0.39075630903244019, 0.61176472902297974, 0.61176472902297974), (0.39495798945426941, 0.60784316062927246, 0.60784316062927246), (0.39915966987609863, 0.60392159223556519, 0.60392159223556519), (0.40336135029792786, 0.60000002384185791, 0.60000002384185791), (0.40756303071975708, 0.59607845544815063, 0.59607845544815063), (0.4117647111415863, 0.58823531866073608, 0.58823531866073608), (0.41596639156341553, 0.58431375026702881, 0.58431375026702881), (0.42016807198524475, 0.58039218187332153, 0.58039218187332153), (0.42436975240707397, 0.57647061347961426, 0.57647061347961426), (0.4285714328289032, 0.57254904508590698, 0.57254904508590698), (0.43277311325073242, 0.56862747669219971, 0.56862747669219971), (0.43697479367256165, 0.56470590829849243, 0.56470590829849243), (0.44117647409439087, 0.56078433990478516, 0.56078433990478516), (0.44537815451622009, 0.55686277151107788, 0.55686277151107788), (0.44957983493804932, 0.55294120311737061, 0.55294120311737061), (0.45378151535987854, 0.54901963472366333, 0.54901963472366333), (0.45798319578170776, 0.54509806632995605, 0.54509806632995605), (0.46218487620353699, 0.54117649793624878, 0.54117649793624878), (0.46638655662536621, 0.5372549295425415, 0.5372549295425415), (0.47058823704719543, 0.53333336114883423, 0.53333336114883423), (0.47478991746902466, 0.52549022436141968, 0.52549022436141968), (0.47899159789085388, 0.5215686559677124, 0.5215686559677124), (0.48319327831268311, 0.51764708757400513, 0.51764708757400513), (0.48739495873451233, 0.51372551918029785, 0.51372551918029785), (0.49159663915634155, 0.50980395078659058, 0.50980395078659058), (0.49579831957817078, 0.5058823823928833, 0.5058823823928833), (0.5, 0.50196081399917603, 0.50196081399917603), (0.50420171022415161, 0.49803921580314636, 0.49803921580314636), (0.50840336084365845, 0.49411764740943909, 0.49411764740943909), (0.51260507106781006, 0.49019607901573181, 0.49019607901573181), (0.51680672168731689, 0.48627451062202454, 0.48627451062202454), (0.52100843191146851, 0.48235294222831726, 0.48235294222831726), (0.52521008253097534, 0.47843137383460999, 0.47843137383460999), (0.52941179275512695, 0.47450980544090271, 0.47450980544090271), (0.53361344337463379, 0.47058823704719543, 0.47058823704719543), (0.5378151535987854, 0.46274510025978088, 0.46274510025978088), (0.54201680421829224, 0.45882353186607361, 0.45882353186607361), (0.54621851444244385, 0.45490196347236633, 0.45490196347236633), (0.55042016506195068, 0.45098039507865906, 0.45098039507865906), (0.55462187528610229, 0.44705882668495178, 0.44705882668495178), (0.55882352590560913, 0.44313725829124451, 0.44313725829124451), (0.56302523612976074, 0.43921568989753723, 0.43921568989753723), (0.56722688674926758, 0.43529412150382996, 0.43529412150382996), (0.57142859697341919, 0.43137255311012268, 0.43137255311012268), (0.57563024759292603, 0.42745098471641541, 0.42745098471641541), (0.57983195781707764, 0.42352941632270813, 0.42352941632270813), (0.58403360843658447, 0.41960784792900085, 0.41960784792900085), (0.58823531866073608, 0.41568627953529358, 0.41568627953529358), (0.59243696928024292, 0.4117647111415863, 0.4117647111415863), (0.59663867950439453, 0.40784314274787903, 0.40784314274787903), (0.60084033012390137, 0.40000000596046448, 0.40000000596046448), (0.60504204034805298, 0.3960784375667572, 0.3960784375667572), (0.60924369096755981, 0.39215686917304993, 0.39215686917304993), (0.61344540119171143, 0.38823530077934265, 0.38823530077934265), (0.61764705181121826, 0.38431373238563538, 0.38431373238563538), (0.62184876203536987, 0.3803921639919281, 0.3803921639919281), (0.62605041265487671, 0.37647059559822083, 0.37647059559822083), (0.63025212287902832, 0.37254902720451355, 0.37254902720451355), (0.63445377349853516, 0.36862745881080627, 0.36862745881080627), (0.63865548372268677, 0.364705890417099, 0.364705890417099), (0.6428571343421936, 0.36078432202339172, 0.36078432202339172), (0.64705884456634521, 0.35686275362968445, 0.35686275362968445), (0.65126049518585205, 0.35294118523597717, 0.35294118523597717), (0.65546220541000366, 0.3490196168422699, 0.3490196168422699), (0.6596638560295105, 0.34509804844856262, 0.34509804844856262), (0.66386556625366211, 0.33725491166114807, 0.33725491166114807), (0.66806721687316895, 0.3333333432674408, 0.3333333432674408), (0.67226892709732056, 0.32941177487373352, 0.32941177487373352), (0.67647057771682739, 0.32549020648002625, 0.32549020648002625), (0.680672287940979, 0.32156863808631897, 0.32156863808631897), (0.68487393856048584, 0.31764706969261169, 0.31764706969261169), (0.68907564878463745, 0.31372550129890442, 0.31372550129890442), (0.69327729940414429, 0.30980393290519714, 0.30980393290519714), (0.6974790096282959, 0.30588236451148987, 0.30588236451148987), (0.70168066024780273, 0.30196079611778259, 0.30196079611778259), (0.70588237047195435, 0.29803922772407532, 0.29803922772407532), (0.71008402109146118, 0.29411765933036804, 0.29411765933036804), (0.71428573131561279, 0.29019609093666077, 0.29019609093666077), (0.71848738193511963, 0.28627452254295349, 0.28627452254295349), (0.72268909215927124, 0.28235295414924622, 0.28235295414924622), (0.72689074277877808, 0.27450981736183167, 0.27450981736183167), (0.73109245300292969, 0.27058824896812439, 0.27058824896812439), (0.73529410362243652, 0.26666668057441711, 0.26666668057441711), (0.73949581384658813, 0.26274511218070984, 0.26274511218070984), (0.74369746446609497, 0.25882354378700256, 0.25882354378700256), (0.74789917469024658, 0.25490197539329529, 0.25490197539329529), (0.75210082530975342, 0.25098040699958801, 0.25098040699958801), (0.75630253553390503, 0.24705882370471954, 0.24705882370471954), (0.76050418615341187, 0.24313725531101227, 0.24313725531101227), (0.76470589637756348, 0.23921568691730499, 0.23921568691730499), (0.76890754699707031, 0.23529411852359772, 0.23529411852359772), (0.77310925722122192, 0.23137255012989044, 0.23137255012989044), (0.77731090784072876, 0.22745098173618317, 0.22745098173618317), (0.78151261806488037, 0.22352941334247589, 0.22352941334247589), (0.78571426868438721, 0.21960784494876862, 0.21960784494876862), (0.78991597890853882, 0.21176470816135406, 0.21176470816135406), (0.79411762952804565, 0.20784313976764679, 0.20784313976764679), (0.79831933975219727, 0.20392157137393951, 0.20392157137393951), (0.8025209903717041, 0.20000000298023224, 0.20000000298023224), (0.80672270059585571, 0.19607843458652496, 0.19607843458652496), (0.81092435121536255, 0.19215686619281769, 0.19215686619281769), (0.81512606143951416, 0.18823529779911041, 0.18823529779911041), (0.819327712059021, 0.18431372940540314, 0.18431372940540314), (0.82352942228317261, 0.18039216101169586, 0.18039216101169586), (0.82773107290267944, 0.17647059261798859, 0.17647059261798859), (0.83193278312683105, 0.17254902422428131, 0.17254902422428131), (0.83613443374633789, 0.16862745583057404, 0.16862745583057404), (0.8403361439704895, 0.16470588743686676, 0.16470588743686676), (0.84453779458999634, 0.16078431904315948, 0.16078431904315948), (0.84873950481414795, 0.15686275064945221, 0.15686275064945221), (0.85294115543365479, 0.14901961386203766, 0.14901961386203766), (0.8571428656578064, 0.14509804546833038, 0.14509804546833038), (0.86134451627731323, 0.14117647707462311, 0.14117647707462311), (0.86554622650146484, 0.13725490868091583, 0.13725490868091583), (0.86974787712097168, 0.13333334028720856, 0.13333334028720856), (0.87394958734512329, 0.12941177189350128, 0.12941177189350128), (0.87815123796463013, 0.12549020349979401, 0.12549020349979401), (0.88235294818878174, 0.12156862765550613, 0.12156862765550613), (0.88655459880828857, 0.11764705926179886, 0.11764705926179886), (0.89075630903244019, 0.11372549086809158, 0.11372549086809158), (0.89495795965194702, 0.10980392247438431, 0.10980392247438431), (0.89915966987609863, 0.10588235408067703, 0.10588235408067703), (0.90336132049560547, 0.10196078568696976, 0.10196078568696976), (0.90756303071975708, 0.098039217293262482, 0.098039217293262482), (0.91176468133926392, 0.094117648899555206, 0.094117648899555206), (0.91596639156341553, 0.086274512112140656, 0.086274512112140656), (0.92016804218292236, 0.08235294371843338, 0.08235294371843338), (0.92436975240707397, 0.078431375324726105, 0.078431375324726105), (0.92857140302658081, 0.074509806931018829, 0.074509806931018829), (0.93277311325073242, 0.070588238537311554, 0.070588238537311554), (0.93697476387023926, 0.066666670143604279, 0.066666670143604279), (0.94117647409439087, 0.062745101749897003, 0.062745101749897003), (0.94537812471389771, 0.058823529630899429, 0.058823529630899429), (0.94957983493804932, 0.054901961237192154, 0.054901961237192154), (0.95378148555755615, 0.050980392843484879, 0.050980392843484879), (0.95798319578170776, 0.047058824449777603, 0.047058824449777603), (0.9621848464012146, 0.043137256056070328, 0.043137256056070328), (0.96638655662536621, 0.039215687662363052, 0.039215687662363052), (0.97058820724487305, 0.035294119268655777, 0.035294119268655777), (0.97478991746902466, 0.031372550874948502, 0.031372550874948502), (0.97899156808853149, 0.023529412224888802, 0.023529412224888802), (0.98319327831268311, 0.019607843831181526, 0.019607843831181526), (0.98739492893218994, 0.015686275437474251, 0.015686275437474251), (0.99159663915634155, 0.011764706112444401, 0.011764706112444401), (0.99579828977584839, 0.0078431377187371254, 0.0078431377187371254), (1.0, 0.0039215688593685627, 0.0039215688593685627)], ) return ColorMapper.from_segment_map(_data, range=range, **traits) def CubicYF(range, **traits): """ Generator of the 'CubicYF' colormap from Matteo Niccoli. Lab-based rainbow scheme with cubic-law luminance. http://mycarta.wordpress.com/color-palettes/ """ palette = array([ [0.5151, 0.0482, 0.6697], [0.5199, 0.1762, 0.8083], [0.4884, 0.2912, 0.9234], [0.4297, 0.3855, 0.9921], [0.3893, 0.4792, 0.9775], [0.3337, 0.5650, 0.9056], [0.2795, 0.6419, 0.8287], [0.2210, 0.7123, 0.7258], [0.2468, 0.7612, 0.6248], [0.2833, 0.8125, 0.5069], [0.3198, 0.8492, 0.3956], [0.3602, 0.8896, 0.2919], [0.4568, 0.9136, 0.3018], [0.6033, 0.9255, 0.3295], [0.7066, 0.9255, 0.3414], [0.8000, 0.9255, 0.3529], ]) return ColorMapper.from_palette_array(palette, range=range, **traits) def CubicL(range, **traits): """ Generator of the 'CubicL' colormap from Matteo Niccoli. Lab-based rainbow scheme with cubic-law luminance, like `CubicYF` but with red at the high end, a modest deviation from being completely perceptual. http://mycarta.wordpress.com/color-palettes/ """ palette = array([ [0.4706, 0.0000, 0.5216], [0.5137, 0.0527, 0.7096], [0.4942, 0.2507, 0.8781], [0.4296, 0.3858, 0.9922], [0.3691, 0.5172, 0.9495], [0.2963, 0.6191, 0.8515], [0.2199, 0.7134, 0.7225], [0.2643, 0.7836, 0.5756], [0.3094, 0.8388, 0.4248], [0.3623, 0.8917, 0.2858], [0.5200, 0.9210, 0.3137], [0.6800, 0.9255, 0.3386], [0.8000, 0.9255, 0.3529], [0.8706, 0.8549, 0.3608], [0.9514, 0.7466, 0.3686], [0.9765, 0.5887, 0.3569], ]) return ColorMapper.from_palette_array(palette, range=range, **traits) def LinearL(range, **traits): """ Generator of the 'LinearL' colormap from Matteo Niccoli. Lab-based linear lightness rainbow. http://mycarta.wordpress.com/color-palettes/ """ palette = array([ [0.0143, 0.0143, 0.0143], [0.1413, 0.0555, 0.1256], [0.1761, 0.0911, 0.2782], [0.1710, 0.1314, 0.4540], [0.1074, 0.2234, 0.4984], [0.0686, 0.3044, 0.5068], [0.0008, 0.3927, 0.4267], [0.0000, 0.4763, 0.3464], [0.0000, 0.5565, 0.2469], [0.0000, 0.6381, 0.1638], [0.2167, 0.6966, 0.0000], [0.3898, 0.7563, 0.0000], [0.6912, 0.7795, 0.0000], [0.8548, 0.8041, 0.4555], [0.9712, 0.8429, 0.7287], [0.9692, 0.9273, 0.8961], ]) return ColorMapper.from_palette_array(palette, range=range, **traits) def LinearLHot(range, **traits): """ Generator of the 'LinearLHot' colormap from Matteo Niccoli. Linear lightness modification of the `hot` colormap. http://mycarta.wordpress.com/color-palettes/ """ palette = array([ [0.0225, 0.0121, 0.0121], [0.1927, 0.0225, 0.0311], [0.3243, 0.0106, 0.0000], [0.4463, 0.0000, 0.0091], [0.5706, 0.0000, 0.0737], [0.6969, 0.0000, 0.1337], [0.8213, 0.0000, 0.1792], [0.8636, 0.0000, 0.0565], [0.8821, 0.2555, 0.0000], [0.8720, 0.4182, 0.0000], [0.8424, 0.5552, 0.0000], [0.8031, 0.6776, 0.0000], [0.7659, 0.7870, 0.0000], [0.8170, 0.8296, 0.0000], [0.8853, 0.8896, 0.4113], [0.9481, 0.9486, 0.7165], ]) return ColorMapper.from_palette_array(palette, range=range, **traits) def CoolWarm(range, **traits): """ Generator of Kenneth Moreland's CoolWarm colormap. Blue-White-Red with smooth lightness transitions. Good for applying to 3D surfaces or otherwise have extra shading applied. http://www.sandia.gov/~kmorel/documents/ColorMaps/ColorMapsExpanded.pdf """ cool = array([59, 76, 192]) / 255.0 warm = array([180, 4, 38]) / 255.0 palette = generate_diverging_palette(cool, warm, 256) return ColorMapper.from_palette_array(palette, range=range, **traits) def CubeHelix(range, **traits): """ Generator of Dave Green's CubeHelix colormap. Sequential colormap with a linear lightness increasing from black to white deviating away from gray in a tapered helix. https://www.mrao.cam.ac.uk/~dag/CUBEHELIX/ """ palette = generate_cubehelix_palette() return ColorMapper.from_palette_array(palette, range=range, **traits) # Make the convenient list of all the function names as well as a dictionary # of name->function mappings. These are useful for UI editors. color_map_functions = [ jet, autumn, bone, cool, copper, flag, gray, yarg, hot, hsv, pink, prism, spring, summer, winter, cw1_004, cw1_005, cw1_006, cw1_028, gmt_drywet, Spectral, RdBu, RdPu, YlGnBu, RdYlBu, GnBu, RdYlGn, PuBu, BuGn, Greens, PRGn, BuPu, OrRd, Oranges, PiYG, YlGn, BrBG, Reds, RdGy, PuRd, Blues, Greys, YlOrRd, YlOrBr, Purples, PuOr, PuBuGn, gist_earth, gist_gray, gist_heat, gist_ncar, gist_rainbow, gist_stern, gist_yarg, CubicYF, CubicL, LinearL, LinearLHot, CoolWarm, CubeHelix, ] color_map_dict = {} for func in color_map_functions: color_map_dict[func] = func.__name__ color_map_name_dict = {} for func in color_map_functions: color_map_name_dict[func.__name__] = func __all__.append(func.__name__) #### EOF ###################################################################### chaco-4.5.0/chaco/default_colors.py0000644000076600000240000000336112426452312020015 0ustar jrocherstaff00000000000000"""List of nice color palettes for Chaco""" # This is a palette of 10 nice colors to use for mapping/discrete # color differentiation. From ColorBrewer. cbrewer = [ (0.65098039, 0.80784314, 0.89019608, 1.0), (0.12156863, 0.47058824, 0.70588235, 1.0), (0.69803922, 0.8745098 , 0.54117647, 1.0), (0.2 , 0.62745098, 0.17254902, 1.0), (0.98431373, 0.60392157, 0.6 , 1.0), (0.89019608, 0.10196078, 0.10980392, 1.0), (0.99215686, 0.74901961, 0.43529412, 1.0), (1. , 0.49803922, 0. , 1.0), (0.79215686, 0.69803922, 0.83921569, 1.0), (0.41568627, 0.23921569, 0.60392157, 1.0), ] palette11 = [ (0.725490, 0.329412, 0.615686, 1.0), (0.121569, 0.313725, 0.552941, 1.0), (0.376471, 0.525490, 0.082353, 1.0), (0.435294, 0.380392, 0.572549, 1.0), (0.988235, 0.400000, 0.600000, 1.0), (0.133333, 0.588235, 0.976471, 1.0), (0.992157, 0.600000, 0.400000, 1.0), (0.611765, 0.200000, 0.380392, 1.0), (0.388235, 0.647059, 0.537255, 1.0), (0.545098, 0.686275, 0.874510, 1.0), (0.623529, 0.501961, 0.862745, 1.0), ] palette14 = [ (0.286275, 0.235294, 0.545098, 1.0), (0.976471, 0.709804, 0.313725, 1.0), (0.850980, 0.094118, 0.521569, 1.0), (0.431373, 0.662745, 0.431373, 1.0), (0.803922, 0.345098, 0.345098, 1.0), (0.015686, 0.749020, 0.403922, 1.0), (0.694118, 0.686275, 0.580392, 1.0), (0.376471, 0.298039, 0.788235, 1.0), (0.992157, 0.396078, 0.011765, 1.0), (0.298039, 0.776471, 0.615686, 1.0), (0.988235, 0.407843, 0.686275, 1.0), (0.000000, 0.600000, 0.984314, 1.0), (0.470588, 0.917647, 0.478431, 1.0), (0.627451, 0.250980, 0.815686, 1.0), ] PALETTES = [cbrewer, palette11, palette14] chaco-4.5.0/chaco/errorbar_plot.py0000644000076600000240000001212212426452312017657 0ustar jrocherstaff00000000000000 from __future__ import with_statement # Major library imports from numpy import column_stack, compress, invert, isnan, transpose import logging # Enthought library imports from traits.api import Any, Enum, Float, Instance # Chaco imports from lineplot import LinePlot from abstract_data_source import AbstractDataSource # Set up a logger for this module logger = logging.getLogger(__name__) class ErrorBarPlot(LinePlot): """ Renders errorbars at various points. """ # The datasource containing the low values value_low = Instance(AbstractDataSource) # The datasource containing the high values value_high = Instance(AbstractDataSource) # The screen-space width of the endcap bars endcap_size = Float(5.0) # The kind of encap to render on error bars endcap_style = Enum("bar", "none", None) # Override the inherited trait definition _cached_data_pts = Any def map_screen(self, data_array): """ data_array can be Nx2 or Nx3. In the former case, each row is treated as (index, value), and this method returns screen X and Y coordinates. In the latter case, each row is treated as (index, value_low, value_high), and the method returns either (x, ylow, yhigh) or (y, xlow, xhigh) depending on self.orientation. """ if len(data_array) == 0: return [] elif data_array.shape[1] == 2: return LinePlot.map_screen(self, data_array) else: x, ylow, yhigh = transpose(data_array) sx = self.index_mapper.map_screen(x) sylow = self.value_mapper.map_screen(ylow) syhigh = self.value_mapper.map_screen(yhigh) return column_stack((sx, sylow, syhigh)) def get_screen_points(self): self._gather_points() return self.map_screen(self._cached_data_pts) def _gather_points(self): if self._cache_valid: return if not self.index or not self.value_low or not self.value_high: return index, index_mask = self.index.get_data_mask() value_low, value_low_mask = self.value_low.get_data_mask() value_high, value_high_mask = self.value_high.get_data_mask() value_mask = value_low_mask & value_high_mask l1, l2, l3 = map(len, (index, value_low, value_high)) if 0 in (l1, l2, l3) or not (l1 == l2 == l3): logger.warn("Chaco: using empty dataset; index_len=%d, value_low_len=%d, value_high_len=%d." % (l1,l2,l3)) self._cached_data_pts = [] self._cache_valid = True return index_range_mask = self.index_mapper.range.mask_data(index) value_low_mask = self.value_mapper.range.mask_data(value_low) value_high_mask = self.value_mapper.range.mask_data(value_high) value_range_mask = value_low_mask | value_high_mask nan_mask = invert(isnan(index_mask) | isnan(value_mask)) point_mask = index_mask & value_mask & nan_mask & index_range_mask & value_range_mask points = column_stack((index, value_low, value_high)) self._cached_data_pts = compress(point_mask, points, axis=0) self._cache_valid = True return def _render(self, gc, points, icon_mode=False): if len(points) == 0: return if not icon_mode: gc.clip_to_rect(self.x, self.y, self.width, self.height) with gc: gc.set_antialias(False) gc.set_stroke_color(self.color_) gc.set_line_width(self.line_width) gc.set_line_dash(self.line_style_) if self.orientation == "h": x, ylow, yhigh = transpose(points) start, end = column_stack((x, ylow)), column_stack((x, yhigh)) gc.line_set(start, end) axis = 0 low = ylow high = yhigh else: y, xlow, xhigh = transpose(points) start, end = column_stack((xlow, y)), column_stack((xhigh, y)) gc.line_set(start, end) axis = 1 low = xlow high = xhigh if self.endcap_style == "bar": self._render_bar_endcap(gc, start, end, low, high, axis) else: gc.stroke_path() if not icon_mode: self._draw_default_axes(gc) return def _render_bar_endcap(self, gc, start, end, low, high, axis): """ Renders the endcaps for endcap_style == "bar". start and end are the two endpoints of the bare errorbar. axis is the column index corresponding to the index direction, so for orientation of 'h', axis is 0. This method modifies start and end. """ delta = self.endcap_size / 2.0 start[:,axis] -= delta end[:,axis] += delta start[:,1-axis] = low end[:,1-axis] = low gc.line_set(start, end) start[:,1-axis] = high end[:,1-axis] = high gc.line_set(start, end) gc.stroke_path() return def _render_icon(self, gc, x, y, width, height): pass chaco-4.5.0/chaco/example_support.py0000644000076600000240000001155712426452312020245 0ustar jrocherstaff00000000000000doc = \ """ This file contains a support class that wraps up the boilerplate toolkit calls that virtually all the demo programs have to use, and doesn't actually do anything when run on its own. Try running simple_line.py, colormapped_scatter.py, or check out any of the programs in in tutorials/. """ from numpy import array from traits.etsconfig.api import ETSConfig # Set up the debug logger for all chaco examples. # We don't want users to go digging around for the default Enthought logfile # in ~/envisage.log, so we add a handler to the global logger for a file # "chaco.log" in the current directory. #import logging, logging.handlers #try: # chaco_handler = logging.handlers.RotatingFileHandler("chaco.log", # maxBytes=1000000, backupCount=0) # logging.getLogger().addHandler(chaco_handler) #except: # # If we can't override the default handler, it's OK. # pass # Import a default palette for backwards compatibility from default_colors import cbrewer as COLOR_PALETTE # FIXME - it should be enough to do the following import, but because of the # PyQt/traits problem (see below) we can't because it would drag in traits too # early. Until it is fixed we just assume wx if we can import it. # Force the selection of a valid toolkit. #import enable.toolkit if not ETSConfig.toolkit: for toolkit, toolkit_module in (('wx', 'wx'), ('qt4', 'pyface.qt')): try: __import__(toolkit_module) ETSConfig.toolkit = toolkit break except ImportError: pass else: raise RuntimeError("Can't load wx or qt4 backend for Chaco.") if ETSConfig.toolkit == 'wx': import wx class DemoFrame(wx.Frame): """ Wraps boilerplate WX calls that almost all the demo programs have to use. """ def __init__ ( self, *args, **kw ): wx.Frame.__init__( *(self,) + args, **kw ) self.SetAutoLayout( True ) # Create the subclass's window self.plot_window = self._create_window() sizer = wx.BoxSizer(wx.HORIZONTAL) sizer.Add(self.plot_window.control, 1, wx.EXPAND) self.SetSizer(sizer) self.Show( True ) return def _create_window(self): "Subclasses should override this method and return an enable.wx.Window" raise NotImplementedError def demo_main(demo_class, size=(400,400), title="Chaco plot"): "Takes the class of the demo to run as an argument." app = wx.PySimpleApp() frame = demo_class(None, size=size, title=title) app.SetTopWindow(frame) app.MainLoop() elif ETSConfig.toolkit == 'qt4': from pyface.qt import QtGui _app = QtGui.QApplication.instance() if _app is None: import sys _app = QtGui.QApplication(sys.argv) class DemoFrame(QtGui.QWidget): def __init__ (self, parent, **kw): QtGui.QWidget.__init__(self) # Create the subclass's window self.plot_window = self._create_window() layout = QtGui.QVBoxLayout() layout.setMargin(0) layout.addWidget(self.plot_window.control) self.setLayout(layout) if 'size' in kw: self.resize(*kw['size']) if 'title' in kw: self.setWindowTitle(kw['title']) self.show() def _create_window(self): "Subclasses should override this method and return an enable.Window" raise NotImplementedError def demo_main(demo_class, size=(400,400), title="Chaco plot"): "Takes the class of the demo to run as an argument." frame = demo_class(None, size=size, title=title) _app.exec_() elif ETSConfig.toolkit == 'pyglet': from enable.pyglet_backend.pyglet_app import get_app, PygletApp class DemoFrame(object): def __init__(self): app = get_app() if app: window = self._create_window() self.enable_win = window app.add_window(window.control) return def _create_window(self): raise NotImplementedError def demo_main(demo_class, size=(640,480), title="Chaco Example"): """ Runs a simple application in Pyglet using an instance of **demo_class** as the main window or frame. **demo_class** should be a subclass of DemoFrame or the pyglet backend's Window class. """ app = PygletApp() if issubclass(demo_class, DemoFrame): frame = demo_class() window = frame.enable_win.control else: window = demo_class().control if not window._fullscreen: window.set_size(*size) window.set_caption(title) app.set_main_window(window) app.run() if __name__ == "__main__": print "\n" + doc + "\n" # EOF chaco-4.5.0/chaco/filled_line_plot.py0000644000076600000240000001040712426452312020313 0ustar jrocherstaff00000000000000 from __future__ import with_statement from numpy import empty from traits.api import Property, Enum # Local imports from lineplot import LinePlot from polygon_plot import PolygonPlot def Alias(name): return Property(lambda obj: getattr(obj, name), lambda obj, val: setattr(obj, name, val)) class FilledLinePlot(PolygonPlot): """ Draws a line plot filled to the axis """ fill_color = Alias("face_color") # Direction to fill. Down is towards the origin, up is towards the max fill_direction = Enum("down", "up") # The rendering style of the line plot. # # connectedpoints # "normal" style (default); each point is connected to subsequent and # prior points by line segments # hold # each point is represented by a line segment parallel to the abscissa # (index axis) and spanning the length between the point and its # subsequent point. # connectedhold # like "hold" style, but line segments are drawn at each point of the # plot to connect the hold lines of the prior point and the current # point. Also called a "right angle plot". render_style = Enum("connectedpoints", "hold", "connectedhold") def _render(self, gc, points): if len(points) == 0: return render_method_dict = { "hold": LinePlot._render_hold, "connectedhold": LinePlot._render_connected_hold, "connectedpoints": LinePlot._render_normal } render_lines = render_method_dict.get(self.render_style, LinePlot._render_normal) if self.fill_direction == 'down': ox, oy = self.map_screen([[0,0]])[0] else: ox, oy = self.map_screen([[self.x_mapper.range.high, self.y_mapper.range.high]])[0] with gc: gc.clip_to_rect(self.x, self.y, self.width, self.height) # If the fill color is not transparent, then draw the fill polygon first face_col = self.effective_face_color if not (len(face_col) == 4 and face_col[-1] == 0): if self.render_style in ("hold","connectedhold"): # Modify the points array before passing it in to render_polys: # Between every two points, create an intermediate point with # the first point's Y and the second point's X. (For vertical # plots, use the first point's X and the second point's Y.) new_points = empty((points.shape[0]*2-1, 2)) new_points[::2] = points if self.orientation == "h": new_points[1::2,0] = points[1:,0] new_points[1::2,1] = points[:-1,1] else: new_points[1::2,0] = points[:-1,0] new_points[1::2,1] = points[1:,1] points = new_points self._render_polys(gc, points, ox, oy) # If the line color is not transparent, or tha same color # as the filled area: edge_col = self.effective_edge_color if (not (len(edge_col) == 4 and edge_col[-1] == 0)) and edge_col != face_col: gc.set_stroke_color(edge_col) gc.set_line_width(self.edge_width) gc.set_line_dash(self.edge_style_) # Create a list around points because the LinePlot supports # Nans, and its rendering methods expect lists of disjoint arrays. render_lines(gc, [points], self.orientation) def _render_polys(self, gc, points, ox, oy): face_col = self.effective_face_color gc.set_fill_color(face_col) gc.begin_path() startx, starty = points[0] if self.orientation == "h": gc.move_to(startx, oy) gc.line_to(startx, starty) else: gc.move_to(ox, starty) gc.line_to(startx, starty) gc.lines(points) endx, endy = points[-1] if self.orientation == "h": gc.line_to(endx, oy) gc.line_to(startx, oy) else: gc.line_to(ox, endy) gc.line_to(ox, starty) gc.close_path() gc.fill_path() chaco-4.5.0/chaco/function_data_source.py0000644000076600000240000000260212426452312021203 0ustar jrocherstaff00000000000000 from numpy import array # Enthought library imports from traits.api import Callable, Instance, on_trait_change # Local, relative imports from abstract_data_source import AbstractDataSource from array_data_source import ArrayDataSource from data_range_1d import DataRange1D class FunctionDataSource(ArrayDataSource): # The function to call with the low and high values of the range. # It should return an array of values. func = Callable # A reference to a datarange data_range = Instance(DataRange1D) def __init__(self, **kw): # Explicitly call the AbstractDataSource constructor because # the ArrayDataSource ctor wants a data array AbstractDataSource.__init__(self, **kw) self.recalculate() @on_trait_change('data_range.updated') def recalculate(self): if self.func is not None and self.data_range is not None: newarray = self.func(self.data_range.low, self.data_range.high) ArrayDataSource.set_data(self, newarray) else: self._data = array([], dtype=float) def set_data(self, *args, **kw): raise RuntimeError("Cannot set numerical data on a FunctionDataSource") def set_mask(self, mask): # This would be REALLY FREAKING SLICK, but it's current unimplemented raise NotImplementedError def remove_mask(self): raise NotImplementedError chaco-4.5.0/chaco/function_image_data.py0000644000076600000240000000371612426452312020774 0ustar jrocherstaff00000000000000from numpy import array from traits.api import Instance, Callable, on_trait_change from chaco.api import DataRange2D, ImageData # Adapted (ie. copied and modified) from function_data_source. # Given the time frequently required for image manipulation, # it would be awesome if there was a mechanism for returning # partial results as they become available. class FunctionImageData(ImageData): """ A class that provides data for a 2-D image based upon the range supplied. This class can be used as the data source for an image plot or contour plot. Computation should be fairly swift for acceptable interactive performance. """ # The function to call with the low and high values of the range # in the x and y dimensions. It should return either a 2-D array # of numerical values, or an array of RGB or RGBA values (shape should # be (n, m), (n, m, 3) or (n, m, 4)). func = Callable # the 2D data_range required for the data shown data_range = Instance(DataRange2D) def __init__(self, **kw): super(FunctionImageData, self).__init__(**kw) # Explicitly construct the initial data set for ImageData self.recalculate() @on_trait_change('data_range.updated') def recalculate(self): if self.func is not None and self.data_range is not None: newarray = self.func( self.data_range.x_range.low, self.data_range.x_range.high, self.data_range.y_range.low, self.data_range.y_range.high ) ImageData.set_data(self, newarray) else: self._data = array([], dtype=float) def set_data(self, *args, **kw): raise RuntimeError("Cannot set numerical data on a FunctionImageData") def set_mask(self, mask): # This would be REALLY FREAKING SLICK, but it's currently unimplemented raise NotImplementedError def remove_mask(self): raise NotImplementedError chaco-4.5.0/chaco/grid.py0000644000076600000240000003605212426452312015740 0ustar jrocherstaff00000000000000""" Defines the PlotGrid class, and associated Traits UI View and validator function. """ from __future__ import with_statement from numpy import around, array, asarray, column_stack, float64, inf, zeros, zeros_like # Enthought library imports from enable.api import black_color_trait, LineStyle from traits.api import Any, Bool, Callable, Enum, Float, Instance, \ CInt, Trait, Property, TraitError, Tuple, on_trait_change from traitsui.api import HGroup, Item, VGroup, View, TextEditor # Local, relative imports from abstract_overlay import AbstractOverlay from abstract_mapper import AbstractMapper from log_mapper import LogMapper from ticks import AbstractTickGenerator, DefaultTickGenerator def float_or_auto(val): """ Validator function that returns *val* if *val* is either a number or the word 'auto'. This is used as a validator for the text editor in the traits UI for the tick_interval trait. """ try: return float(val) except: if isinstance(val, basestring) and val == "auto": return val raise TraitError, "Tick interval must be a number or 'auto'." # View for setting grid properties. GridView = View(VGroup( HGroup(Item("grid_interval", label="Interval", editor=TextEditor(evaluate=float_or_auto)), Item("visible", label="Visible")), Item("line_color", label="Color", style="custom"), Item("line_style", label="Dash style"), Item("line_width", label="Thickness") ), buttons = ["OK", "Cancel"] ) def Alias(name): return Property(lambda obj: getattr(obj, name), lambda obj, val: setattr(obj, name, val)) class PlotGrid(AbstractOverlay): """ An overlay that represents a grid. A grid is a set of parallel lines, horizontal or vertical. You can use multiple grids with different settings for the horizontal and vertical lines in a plot. """ #------------------------------------------------------------------------ # Data-related traits #------------------------------------------------------------------------ # The mapper (and associated range) that drive this PlotGrid. mapper = Instance(AbstractMapper) # The dataspace interval between grid lines. grid_interval = Trait('auto', 'auto', Float) # The dataspace value at which to start this grid. If None, then # uses the mapper.range.low. data_min = Trait(None, None, Float) # The dataspace value at which to end this grid. If None, then uses # the mapper.range.high. data_max = Trait(None, None, Float) # A callable that implements the AbstractTickGenerator Interface. tick_generator = Instance(AbstractTickGenerator) #------------------------------------------------------------------------ # Layout traits #------------------------------------------------------------------------ # The orientation of the grid lines. "horizontal" means that the grid # lines are parallel to the X axis and the ticker and grid interval # refer to the Y axis. orientation = Enum('horizontal', 'vertical') # Draw the ticks starting at the end of the mapper range? If False, the # ticks are drawn starting at 0. This setting can be useful to keep the # grid from from "flashing" as the user resizes the plot area. flip_axis = Bool(False) # Optional specification of the grid bounds in the dimension transverse # to the ticking/gridding dimension, i.e. along the direction specified # by self.orientation. If this is specified but transverse_mapper is # not specified, then there is no effect. # # None : use self.bounds or self.component.bounds (if overlay) # Tuple : (low, high) extents, used for every grid line # Callable : Function that takes an array of dataspace grid ticks # and returns either an array of shape (N,2) of (starts,ends) # for each grid point or a single tuple (low, high) transverse_bounds = Trait(None, Tuple, Callable) # Mapper in the direction corresponding to self.orientation, i.e. transverse # to the direction of self.mapper. This is used to compute the screen # position of transverse_bounds. If this is not specified, then # transverse_bounds has no effect, and vice versa. transverse_mapper = Instance(AbstractMapper) # Dimensions that the grid is resizable in (overrides PlotComponent). resizable = "hv" #------------------------------------------------------------------------ # Appearance traits #------------------------------------------------------------------------ # The color of the grid lines. line_color = black_color_trait # The style (i.e., dash pattern) of the grid lines. line_style = LineStyle('solid') # The thickness, in pixels, of the grid lines. line_width = CInt(1) line_weight = Alias("line_width") # Default Traits UI View for modifying grid attributes. traits_view = GridView #------------------------------------------------------------------------ # Private traits; mostly cached information #------------------------------------------------------------------------ _cache_valid = Bool(False) _tick_list = Any _tick_positions = Any # An array (N,2) of start,end positions in the transverse direction # i.e. the direction corresponding to self.orientation _tick_extents = Any #_length = Float(0.0) #------------------------------------------------------------------------ # Public methods #------------------------------------------------------------------------ def __init__(self, **traits): # TODO: change this back to a factory in the instance trait some day self.tick_generator = DefaultTickGenerator() super(PlotGrid, self).__init__(**traits) self.bgcolor = "none" #make sure we're transparent return @on_trait_change("bounds,bounds_items,position,position_items") def invalidate(self): """ Invalidate cached information about the grid. """ self._reset_cache() return #------------------------------------------------------------------------ # PlotComponent and AbstractOverlay interface #------------------------------------------------------------------------ def do_layout(self, *args, **kw): """ Tells this component to do layout at a given size. Overrides PlotComponent. """ if self.use_draw_order and self.component is not None: self._layout_as_overlay(*args, **kw) else: super(PlotGrid, self).do_layout(*args, **kw) return #------------------------------------------------------------------------ # Private methods #------------------------------------------------------------------------ def _do_layout(self): """ Performs a layout. Overrides PlotComponent. """ return def _layout_as_overlay(self, size=None, force=False): """ Lays out the axis as an overlay on another component. """ if self.component is not None: self.position = self.component.position self.bounds = self.component.bounds return def _reset_cache(self): """ Clears the cached tick positions. """ self._tick_positions = array([], dtype=float) self._tick_extents = array([], dtype=float) self._cache_valid = False return def _compute_ticks(self, component=None): """ Calculates the positions for the grid lines. """ if (self.mapper is None): self._reset_cache() self._cache_valid = True return if self.data_min is None: datalow = self.mapper.range.low else: datalow = self.data_min if self.data_max is None: datahigh = self.mapper.range.high else: datahigh = self.data_max # Map the low and high data points screenhigh = self.mapper.map_screen(datalow) screenlow = self.mapper.map_screen(datahigh) if (datalow == datahigh) or (screenlow == screenhigh) or \ (datalow in [inf, -inf]) or (datahigh in [inf, -inf]): self._reset_cache() self._cache_valid = True return if component is None: component = self.component if component is not None: bounds = component.bounds position = component.position else: bounds = self.bounds position = self.position if isinstance(self.mapper, LogMapper): scale = 'log' else: scale = 'linear' ticks = self.tick_generator.get_ticks(datalow, datahigh, datalow, datahigh, self.grid_interval, use_endpoints = False, scale=scale) tick_positions = self.mapper.map_screen(array(ticks, float64)) if self.orientation == 'horizontal': self._tick_positions = around(column_stack((zeros_like(tick_positions) + position[0], tick_positions))) elif self.orientation == 'vertical': self._tick_positions = around(column_stack((tick_positions, zeros_like(tick_positions) + position[1]))) else: raise self.NotImplementedError # Compute the transverse direction extents self._tick_extents = zeros((len(ticks), 2), dtype=float) if self.transverse_bounds is None or self.transverse_mapper is None: # No mapping needed, just use the extents if self.orientation == 'horizontal': extents = (position[0], position[0] + bounds[0]) elif self.orientation == 'vertical': extents = (position[1], position[1] + bounds[1]) self._tick_extents[:] = extents elif callable(self.transverse_bounds): data_extents = self.transverse_bounds(ticks) tmapper = self.transverse_mapper if isinstance(data_extents, tuple): self._tick_extents[:] = tmapper.map_screen(asarray(data_extents)) else: extents = array([tmapper.map_screen(data_extents[:,0]), tmapper.map_screen(data_extents[:,1])]).T self._tick_extents = extents else: # Already a tuple self._tick_extents[:] = self.transverse_mapper.map_screen(asarray(self.transverse_bounds)) self._cache_valid = True def _draw_overlay(self, gc, view_bounds=None, mode='normal'): """ Draws the overlay layer of a component. Overrides PlotComponent. """ self._draw_component(gc, view_bounds, mode) return def overlay(self, other_component, gc, view_bounds=None, mode="normal"): """ Draws this component overlaid on another component. Overrides AbstractOverlay. """ if not self.visible: return self._compute_ticks(other_component) self._draw_component(gc, view_bounds, mode) self._cache_valid = False return def _draw_component(self, gc, view_bounds=None, mode="normal"): """ Draws the component. This method is preserved for backwards compatibility. Overrides PlotComponent. """ # What we're really trying to do with a grid is plot contour lines in # the space of the plot. In a rectangular plot, these will always be # straight lines. if not self.visible: return if not self._cache_valid: self._compute_ticks() if len(self._tick_positions) == 0: return with gc: gc.set_line_width(self.line_weight) gc.set_line_dash(self.line_style_) gc.set_stroke_color(self.line_color_) gc.set_antialias(False) if self.component is not None: gc.clip_to_rect(*(self.component.position + self.component.bounds)) else: gc.clip_to_rect(*(self.position + self.bounds)) gc.begin_path() if self.orientation == "horizontal": starts = self._tick_positions.copy() starts[:,0] = self._tick_extents[:,0] ends = self._tick_positions.copy() ends[:,0] = self._tick_extents[:,1] else: starts = self._tick_positions.copy() starts[:,1] = self._tick_extents[:,0] ends = self._tick_positions.copy() ends[:,1] = self._tick_extents[:,1] if self.flip_axis: starts, ends = ends, starts gc.line_set(starts, ends) gc.stroke_path() return def _mapper_changed(self, old, new): if old is not None: old.on_trait_change(self.mapper_updated, "updated", remove=True) if new is not None: new.on_trait_change(self.mapper_updated, "updated") self.invalidate() return def mapper_updated(self): """ Event handler that is bound to this mapper's **updated** event. """ self.invalidate() return def _position_changed_for_component(self): self.invalidate() def _position_items_changed_for_component(self): self.invalidate() def _bounds_changed_for_component(self): self.invalidate() def _bounds_items_changed_for_component(self): self.invalidate() #------------------------------------------------------------------------ # Event handlers for visual attributes. These mostly just call request_redraw() #------------------------------------------------------------------------ @on_trait_change("visible,line_color,line_style,line_weight") def visual_attr_changed(self): """ Called when an attribute that affects the appearance of the grid is changed. """ if self.component: self.component.invalidate_draw() self.component.request_redraw() else: self.invalidate_draw() self.request_redraw() def _grid_interval_changed(self): self.invalidate() self.visual_attr_changed() def _orientation_changed(self): self.invalidate() self.visual_attr_changed() return ### Persistence ########################################################### #_pickles = ("orientation", "line_color", "line_style", "line_weight", # "grid_interval", "mapper") def __getstate__(self): state = super(PlotGrid,self).__getstate__() for key in ['_cache_valid', '_tick_list', '_tick_positions', '_tick_extents']: if state.has_key(key): del state[key] return state def _post_load(self): super(PlotGrid, self)._post_load() self._mapper_changed(None, self.mapper) self._reset_cache() self._cache_valid = False return # EOF chaco-4.5.0/chaco/grid_data_source.py0000644000076600000240000001174112426452312020307 0ustar jrocherstaff00000000000000""" Defines the GridDataSource class. """ # Major library imports from numpy import array # Enthougth library imports from traits.api import Constant, Instance, Tuple # Chaco imports from abstract_data_source import AbstractDataSource from array_data_source import ArrayDataSource from base import SortOrderTrait class GridDataSource(AbstractDataSource): """ Implements a structured gridded 2-D data source (suitable as an index for an image, for example). """ #------------------------------------------------------------------------ # AbstractDataSource traits #------------------------------------------------------------------------ # The dimensionality of the indices into this data source (overrides # AbstractDataSource). index_dimension = Constant('image') # The dimensionality of the value at each index point (overrides # AbstractDataSource). value_dimension = Constant('scalar') # The sort order of the data (overrides AbstractDataSource). There is no # overall sort order on 2-D data, but for gridded 2-D data, each axis can # have a sort order. sort_order =Tuple(SortOrderTrait, SortOrderTrait) #------------------------------------------------------------------------ # Private traits #------------------------------------------------------------------------ # Data grid ticks along the x (horizontal) axis. _xdata = Instance(ArrayDataSource, args=()) # Data grid ticks along the y (vertical) axis _ydata = Instance(ArrayDataSource, args=()) # Cached values of min and max as long as **_data** doesn't change # (overrides ArrayDataSource). ((min_x, max_x), (min_y, max_y)) _cached_bounds = Tuple #------------------------------------------------------------------------ # Public methods #------------------------------------------------------------------------ def __init__(self, xdata=array([]), ydata=array([]), sort_order=("none","none"), **kwargs): super(GridDataSource, self).__init__(**kwargs) self.set_data(xdata, ydata, sort_order) def set_data(self, xdata, ydata, sort_order=None): """ Sets the data, and optionally the sort order, for this data source. Parameters ---------- xdata, ydata : array The data to use. sort_order : SortOrderTrait The sort order of the data """ if sort_order is not None: self.sort_order = sort_order self._xdata.set_data(xdata, sort_order[0]) self._ydata.set_data(ydata, sort_order[1]) else: self._xdata.set_data(xdata) self._ydata.set_data(ydata) self._compute_bounds() self.data_changed = True #------------------------------------------------------------------------ # AbstractDataSource interface #------------------------------------------------------------------------ def get_data(self): """get_data() -> (xdata, ydata) Implements AbstractDataSource. Because this class uses structured (gridded) data, this method returns the pair of data axes, instead of, for example, a full mesh-grid. This behavious differs from other data sources. """ if self._xdata is not None: xdata = self._xdata else: xdata = ArrayDataSource(array([])) if self._ydata is not None: ydata = self._ydata else: ydata = ArrayDataSource(array([])) return xdata, ydata def get_bounds(self): """get_bounds() -> ((LLx, LLy), (URx, URy)) Implements AbstractDataSource. Returns two 2-D points, min and max, that represent the bounding corners of a rectangle enclosing the data set. Note that these values are not view-dependent, but represent intrinsic properties of the DataSource. If data axis is the empty set, then the min and max valuess are 0.0. """ if self._cached_bounds == (): self._compute_bounds() return self._cached_bounds #------------------------------------------------------------------------ # Private methods #------------------------------------------------------------------------ def _compute_bounds(self, data=None): """ Computes the minimum and maximum points (LLx, LLy) and (URx, URy) of the data. """ if data is None: xdata, ydata = self.get_data() else: xdata, ydata = data xbds = xdata.get_bounds() ybds = ydata.get_bounds() self._cached_bounds = ((xbds[0], ybds[0]), (xbds[1], ybds[1])) #------------------------------------------------------------------------ # Event handlers #------------------------------------------------------------------------ def _metadata_changed(self, event): self.metadata_changed = True def _metadata_items_changed(self, event): self.metadata_changed = True chaco-4.5.0/chaco/grid_mapper.py0000644000076600000240000002232212426462425017305 0ustar jrocherstaff00000000000000""" Defines the GridMapper class, which maps from a 2-D region in data space into a structured (gridded) 1-D output space. """ # Python standard library imports from contextlib import contextmanager # Major library imports from numpy import column_stack, transpose # Enthought library imports from traits.api import Bool, DelegatesTo, Instance, Float, Property # Local relative imports from abstract_mapper import AbstractMapper from base_1d_mapper import Base1DMapper from data_range_2d import DataRange2D from linear_mapper import LinearMapper from log_mapper import LogMapper class GridMapper(AbstractMapper): """ Maps a 2-D data space to and from screen space by specifying a 2-tuple in data space or by specifying a pair of screen coordinates. The mapper concerns itself only with metric and not with orientation. So, to "flip" a screen space orientation, swap the appropriate screen space values for **x_low_pos**, **x_high_pos**, **y_low_pos**, and **y_high_pos**. """ # The data-space bounds of the mapper. range = Instance(DataRange2D) # The screen space position of the lower bound of the horizontal axis. x_low_pos = Float(0.0) # The screen space position of the upper bound of the horizontal axis. x_high_pos = Float(1.0) # The screen space position of the lower bound of the vertical axis. y_low_pos = Float(0.0) # The screen space position of the upper bound of the vertical axis. y_high_pos = Float(1.0) # Convenience property for low and high positions in one structure. # Must be a tuple (x_low_pos, x_high_pos, y_low_pos, y_high_pos). screen_bounds = Property # Should the mapper stretch the dataspace when its screen space bounds are # modified (default), or should it preserve the screen-to-data ratio and # resize the data bounds? If the latter, it will only try to preserve # the ratio if both screen and data space extents are non-zero. stretch_data_x = DelegatesTo("_xmapper", prefix="stretch_data") stretch_data_y = DelegatesTo("_ymapper", prefix="stretch_data") # Should the mapper try to maintain a fixed aspect ratio between x and y maintain_aspect_ratio = Bool # The aspect ratio that we wish to maintain aspect_ratio = Float(1.0) #------------------------------------------------------------------------ # Private Traits #------------------------------------------------------------------------ _updating_submappers = Bool(False) _updating_aspect = Bool(False) _xmapper = Instance(Base1DMapper) _ymapper = Instance(Base1DMapper) #------------------------------------------------------------------------ # Public methods #------------------------------------------------------------------------ def __init__(self, x_type="linear", y_type="linear", range=None, **kwargs): # TODO: This is currently an implicit assumption, i.e. that the range # will be passed in to the constructor. It would be impossible to # create the xmapper and ymapper otherwise. However, this should be # changed so that the mappers get created or modified in response to # the .range attribute changing, instead of requiring the range to # be passed in at construction time. self.range = range if "_xmapper" not in kwargs: if x_type == "linear": self._xmapper = LinearMapper(range=self.range.x_range) elif x_type == "log": self._xmapper = LogMapper(range=self.range.x_range) else: raise ValueError("Invalid x axis type: %s" % x_type) else: self._xmapper = kwargs.pop("_xmapper") if "_ymapper" not in kwargs: if y_type == "linear": self._ymapper = LinearMapper(range=self.range.y_range) elif y_type == "log": self._ymapper = LogMapper(range=self.range.y_range) else: raise ValueError("Invalid y axis type: %s" % y_type) else: self._ymapper = kwargs.pop("_ymapper") # Now that the mappers are created, we can go to the normal HasTraits # constructor, which might set values that depend on us having a valid # range and mappers. super(GridMapper, self).__init__(**kwargs) def map_screen(self, data_pts): """ map_screen(data_pts) -> screen_array Maps values from data space into screen space. """ xs, ys = transpose(data_pts) screen_xs = self._xmapper.map_screen(xs) screen_ys = self._ymapper.map_screen(ys) screen_pts = column_stack([screen_xs, screen_ys]) return screen_pts def map_data(self, screen_pts): """ map_data(screen_pts) -> data_vals Maps values from screen space into data space. """ screen_xs, screen_ys = transpose(screen_pts) xs = self._xmapper.map_data(screen_xs) ys = self._ymapper.map_data(screen_ys) data_pts = column_stack([xs, ys]) return data_pts def map_data_array(self, screen_pts): return self.map_data(screen_pts) #------------------------------------------------------------------------ # Private Methods #------------------------------------------------------------------------ def _update_bounds(self): with self._update_submappers(): self._xmapper.screen_bounds = (self.x_low_pos, self.x_high_pos) self._ymapper.screen_bounds = (self.y_low_pos, self.y_high_pos) self.updated = True def _update_range(self): self.updated = True def _update_aspect_x(self): y_width = self._ymapper.high_pos - self._ymapper.low_pos if y_width == 0: return y_scale = (self._ymapper.range.high - self._ymapper.range.low)/y_width x_range_low = self._xmapper.range.low x_width = self._xmapper.high_pos - self._xmapper.low_pos sign = self._xmapper.sign * self._ymapper.sign if x_width == 0 or sign == 0: return x_scale = sign*y_scale/self.aspect_ratio with self._update_aspect(): self._xmapper.range.set_bounds(x_range_low, x_range_low + x_scale*x_width) def _update_aspect_y(self): x_width = self._xmapper.high_pos - self._xmapper.low_pos if x_width == 0: return x_scale = (self._xmapper.range.high - self._xmapper.range.low)/x_width y_range_low = self._ymapper.range.low y_width = self._ymapper.high_pos-self._ymapper.low_pos sign = self._xmapper.sign * self._ymapper.sign if y_width == 0 or sign == 0: return y_scale = sign*x_scale*self.aspect_ratio with self._update_aspect(): self._ymapper.range.set_bounds(y_range_low, y_range_low + y_scale*y_width) #------------------------------------------------------------------------ # Property handlers #------------------------------------------------------------------------ def _range_changed(self, old, new): if old is not None: old.on_trait_change(self._update_range, "updated", remove=True) if new is not None: new.on_trait_change(self._update_range, "updated") if self._xmapper is not None: self._xmapper.range = new.x_range if self._ymapper is not None: self._ymapper.range = new.y_range self._update_range() def _x_low_pos_changed(self): self._xmapper.low_pos = self.x_low_pos def _x_high_pos_changed(self): self._xmapper.high_pos = self.x_high_pos def _y_low_pos_changed(self): self._ymapper.low_pos = self.y_low_pos def _y_high_pos_changed(self): self._ymapper.high_pos = self.y_high_pos def _set_screen_bounds(self, new_bounds): # TODO: figure out a way to not need to do this check: if self.screen_bounds == new_bounds: return self.set(x_low_pos=new_bounds[0], trait_change_notify=False) self.set(x_high_pos=new_bounds[1], trait_change_notify=False) self.set(y_low_pos=new_bounds[2], trait_change_notify=False) self.set(y_high_pos=new_bounds[3], trait_change_notify=False) self._update_bounds() def _get_screen_bounds(self): return (self.x_low_pos, self.x_high_pos, self.y_low_pos, self.y_high_pos) def _updated_fired_for__xmapper(self): if not self._updating_aspect: if self.maintain_aspect_ratio and self.stretch_data_x: self._update_aspect_y() if not self._updating_submappers: self.updated = True def _updated_fired_for__ymapper(self): if not self._updating_aspect: if self.maintain_aspect_ratio and self.stretch_data_y: self._update_aspect_x() if not self._updating_submappers: self.updated = True @contextmanager def _update_submappers(self): self._updating_submappers = True try: yield finally: self._updating_submappers = False @contextmanager def _update_aspect(self): self._updating_aspect = True try: yield finally: self._updating_aspect = False chaco-4.5.0/chaco/horizon_plot.py0000644000076600000240000000742712426452312017545 0ustar jrocherstaff00000000000000from __future__ import with_statement from numpy import array, transpose, ndarray, empty from traits.api import Instance, DelegatesTo, Bool, Int from enable.api import transparent_color_trait from chaco.color_mapper import ColorMapper from chaco.base_xy_plot import BaseXYPlot from chaco.linear_mapper import LinearMapper class BandedMapper(LinearMapper): bands = Int(4) def map_screen(self, data_array): self._compute_scale() if self._null_data_range: if isinstance(data_array, (tuple, list, ndarray)): x = empty(data_array.shape) x.fill(self.low_pos) return x else: return array([self.low_pos]) else: # Scale the data by the number of bands return (data_array*self.bands - self.range.low) * self._scale + self.low_pos class HorizonPlot(BaseXYPlot): bands = DelegatesTo('value_mapper') color_mapper = Instance(ColorMapper) mirror = Bool(False) # FIXME There should be a way to automatically detect whether the data has # negative bands negative_bands = Bool(True) # Override parent traits orientation = 'h' def _color_mapper_changed(self, new): # change the number of steps to match the number of bands if not self.negative_bands: new.steps = self.bands+1 else: new.steps = self.bands*2+1 def _gather_points(self): """ Collects the data points that are within the bounds of the plot and caches them. """ if self._cache_valid: return index = self.index.get_data() value = self.value.get_data() if not self.index or not self.value: return if len(index) == 0 or len(value) == 0 or len(index) != len(value): self._cached_data_pts = [] self._cache_valid = True return points = transpose(array((index,value))) self._cached_data_pts = points self._cache_valid = True def _render(self, gc, points): if len(points) == 0: return ox, oy = self.map_screen([[0,0]])[0] ylow, yhigh = self.value_mapper.screen_bounds y_plus_height = yhigh - oy # Get color bands bands = array(self.color_mapper._get_color_bands()) with gc: gc.clip_to_rect(self.x, self.y, self.width, self.height) # draw positive bands inc = -1 * array([0, y_plus_height]) if self.negative_bands: render_bands = bands[self.bands+1:] else: render_bands = bands[1:] for i, col in enumerate(render_bands): self._render_fill(gc, col, points+i*inc, ox, oy) # draw negative bands if self.negative_bands: if self.mirror: points[:,1] = oy - points[:,1] zeroy = oy else: points[:,1] += y_plus_height inc *= -1 zeroy = int(yhigh) + 2 for i, col in enumerate(bands[self.bands-1::-1]): self._render_fill(gc, col, points+i*inc, ox, zeroy) gc.set_stroke_color((.75, .75, .75)) gc.set_line_width(2) gc.begin_path() gc.move_to(self.x, self.y) gc.line_to(self.x+self.width, self.y) gc.stroke_path() def _render_fill(self, gc, face_col, points, ox, oy): gc.set_fill_color(tuple(face_col)) gc.begin_path() startx, starty = points[0] gc.move_to(startx, oy) gc.line_to(startx, starty) gc.lines(points) endx, endy = points[-1] gc.line_to(endx, oy) gc.line_to(startx, oy) gc.close_path() gc.fill_path() chaco-4.5.0/chaco/image_data.py0000644000076600000240000001545412426452312017071 0ustar jrocherstaff00000000000000""" Defines the ImageData class. """ # Standard library imports from numpy import nanmax, nanmin, swapaxes # Enthought library imports from traits.api import Bool, Int, Property, ReadOnly, Tuple # Local relative imports from base import DimensionTrait, ImageTrait from abstract_data_source import AbstractDataSource class ImageData(AbstractDataSource): """ Represents a grid of data to be plotted using a Numpy 2-D grid. The data array has dimensions NxM, but it may have more than just 2 dimensions. The appropriate dimensionality of the value array depends on the context in which the ImageData instance will be used. """ # The dimensionality of the data. dimension = ReadOnly(DimensionTrait('image')) # Depth of the values at each i,j. Values that are used include: # # * 3: color images, without alpha channel # * 4: color images, with alpha channel value_depth = Int(1) # TODO: Modify ImageData to explicitly support scalar # value arrays, as needed by CMapImagePlot # Holds the grid data that forms the image. The shape of the array is # (N, M, D) where: # # * D is 1, 3, or 4. # * N is the length of the y-axis. # * M is the length of the x-axis. # # Thus, data[0,:,:] must be the first row of data. If D is 1, then # the array must be of type float; if D is 3 or 4, then the array # must be of type uint8. # # NOTE: If this ImageData was constructed with a transposed data array, # then internally it is still transposed (i.e., the x-axis is the first axis # and the y-axis is the second), and the **data** array property might not be # contiguous. If contiguousness is required and calling copy() is too # expensive, use the **raw_value** attribute. Also note that setting this # trait does not change the value of **transposed**, # so be sure to set it to its proper value when using the same ImageData # instance interchangeably to store transposed and non-transposed data. data = Property(ImageTrait) # Is **raw_value**, the actual underlying image data # array, transposed from **data**? (I.e., does the first axis correspond to # the x-direction and the second axis correspond to the y-direction?) # # Rather than transposing or swapping axes on the data and destroying # continuity, this class exposes the data as both **data** and **raw_value**. transposed = Bool(False) # A read-only attribute that exposes the underlying array. raw_value = Property(ImageTrait) #------------------------------------------------------------------------ # Private traits #------------------------------------------------------------------------ # The actual image data array. Can be MxN or NxM, depending on the value # of **transposed**. _data = ImageTrait # Is the bounds cache valid? If False, it needs to be computed. _bounds_cache_valid = Bool(False) # Cached value of min and max as long as **data** doesn't change. _bounds_cache = Tuple #------------------------------------------------------------------------ # Public methods #------------------------------------------------------------------------ @classmethod def fromfile(cls, filename): """ Alternate constructor to create an ImageData from an image file on disk. 'filename' may be a file path or a file object. """ from kiva.image import Image img = Image(filename) imgdata = cls(data=img.bmp_array, transposed=False) fmt = img.format() if fmt == "rgb24": imgdata.value_depth = 3 elif fmt == "rgba32": imgdata.value_depth = 4 else: raise ValueError("Unknown image format in file %s: %s" % (filename, fmt)) return imgdata def get_width(self): """ Returns the shape of the x-axis. """ if self.transposed: return self._data.shape[0] else: return self._data.shape[1] def get_height(self): """ Returns the shape of the y-axis. """ if self.transposed: return self._data.shape[1] else: return self._data.shape[0] def get_array_bounds(self): """ Always returns ((0, width), (0, height)) for x-bounds and y-bounds. """ if self.transposed: b = ((0,self._data.shape[0]), (0,self._data.shape[1])) else: b = ((0,self._data.shape[1]), (0,self._data.shape[0])) return b #------------------------------------------------------------------------ # Datasource interface #------------------------------------------------------------------------ def get_data(self): """ Returns the data for this data source. Implements AbstractDataSource. """ return self.data def is_masked(self): """is_masked() -> False Implements AbstractDataSource. """ return False def get_bounds(self): """ Returns the minimum and maximum values of the data source's data. Implements AbstractDataSource. """ if not self._bounds_cache_valid: if self.raw_value.size == 0: self._cached_bounds = (0,0) else: self._cached_bounds = (nanmin(self.raw_value), nanmax(self.raw_value)) self._bounds_cache_valid = True return self._cached_bounds def get_size(self): """get_size() -> int Implements AbstractDataSource. """ if self._data is not None and self._data.shape[0] != 0: return self._data.shape[0] * self._data.shape[1] else: return 0 def set_data(self, data): """ Sets the data for this data source. Parameters ---------- data : array The data to use. """ self._set_data(data) #------------------------------------------------------------------------ # Private methods #------------------------------------------------------------------------ def _get_data(self): if self.transposed: return swapaxes(self._data, 0, 1) else: return self._data def _set_data(self, newdata): self._data = newdata self._bounds_cache_valid = False self.data_changed = True def _get_raw_value(self): return self._data #------------------------------------------------------------------------ # Event handlers #------------------------------------------------------------------------ def _metadata_changed(self, event): self.metadata_changed = True def _metadata_items_changed(self, event): self.metadata_changed = True # EOF chaco-4.5.0/chaco/image_plot.py0000644000076600000240000003244312426462410017133 0ustar jrocherstaff00000000000000# # (C) Copyright 2013 Enthought, Inc., Austin, TX # All right reserved. # # This file is open source software distributed according to the terms in # LICENSE.txt # """ Defines the ImagePlot class. """ from __future__ import with_statement # Standard library imports from math import ceil, floor, pi # Enthought library imports. from traits.api import Bool, Either, Enum, Instance, \ List, Range, Trait, Tuple from kiva.agg import GraphicsContextArray # Local relative imports from base_2d_plot import Base2DPlot class ImagePlot(Base2DPlot): """ A plot based on an image. """ #------------------------------------------------------------------------ # Data-related traits #------------------------------------------------------------------------ # Overall alpha value of the image. Ranges from 0.0 for transparent to 1.0 # for full intensity. alpha = Trait(1.0, Range(0.0, 1.0)) # The interpolation method to use when rendering an image onto the GC. interpolation = Enum("nearest", "bilinear", "bicubic") #------------------------------------------------------------------------ # Private traits #------------------------------------------------------------------------ # Are the cache traits valid? If False, new ones need to be computed. _image_cache_valid = Bool(False) # Cached image of the bmp data (not the bmp data in self.data.value). _cached_image = Instance(GraphicsContextArray) # Tuple-defined rectangle (x, y, dx, dy) in screen space in which the # **_cached_image** is to be drawn. _cached_dest_rect = Either(Tuple, List) #------------------------------------------------------------------------ # Base2DPlot interface #------------------------------------------------------------------------ def _render(self, gc): """ Actually draws the plot. Implements the Base2DPlot interface. """ if not self._image_cache_valid: self._compute_cached_image() if "bottom" in self.origin: sy = -1 else: sy = 1 if "left" in self.origin: sx = 1 else: sx = -1 # If the orientation is flipped, the BR and TL cases are swapped if self.orientation == "v" and sx == sy: sx, sy = -sx, -sy with gc: gc.clip_to_rect(self.x, self.y, self.width, self.height) gc.set_alpha(self.alpha) # Kiva image interpolation note: # Kiva's Agg backend uses the interpolation setting of the *source* # image to determine the type of interpolation to use when drawing the # image. The mac backend uses the interpolation setting on the # destination GC. old_interp = self._cached_image.get_image_interpolation() if hasattr(gc, "set_interpolation_quality"): from kiva.quartz.ABCGI import InterpolationQuality interp_quality_dict = {"nearest": InterpolationQuality.none, "bilinear": InterpolationQuality.low, "bicubic": InterpolationQuality.high} gc.set_interpolation_quality(interp_quality_dict[self.interpolation]) elif hasattr(gc, "set_image_interpolation"): self._cached_image.set_image_interpolation(self.interpolation) x, y, w, h = self._cached_dest_rect if self.orientation == "h": # for horizontal orientation: gc.translate_ctm(x+w/2, y+h/2) # translate back normally else: # for vertical orientation: gc.translate_ctm(y+h/2, x+w/2) # translate back with dx,dy swap gc.scale_ctm(sx, sy) # flip axes as appropriate if self.orientation == "v": # for vertical orientation: gc.scale_ctm(1,-1) # restore origin to lower left gc.rotate_ctm(pi/2) # rotate 1/4 turn clockwise gc.translate_ctm(-x-w/2, -y-h/2) # translate image center to origin gc.draw_image(self._cached_image, self._cached_dest_rect) self._cached_image.set_image_interpolation(old_interp) def map_index(self, screen_pt, threshold=0.0, outside_returns_none=True, index_only=False): """ Maps a screen space point to an index into the plot's index array(s). Implements the AbstractPlotRenderer interface. Uses 0.0 for *threshold*, regardless of the passed value. """ # For image plots, treat hittesting threshold as 0.0, because it's # the only thing that really makes sense. return Base2DPlot.map_index(self, screen_pt, 0.0, outside_returns_none, index_only) #------------------------------------------------------------------------ # Private methods #------------------------------------------------------------------------ def _compute_cached_image(self, data=None, mapper=None): """ Computes the correct sub-image coordinates and renders an image into self._cached_image. The parameter *data* is for subclasses that might not store an RGB(A) image as the value, but need to compute one to display (colormaps, etc.). The parameter *mapper* is also for subclasses that might not store an RGB(A) image as their value, and gives an opportunity to produce the values only for the visible region, rather than for the whole plot, at the expense of more frequent computation. """ if data is None: data = self.value.data (lpt, upt) = self.index.get_bounds() ll_x, ll_y = self.map_screen([lpt])[0] ur_x, ur_y = self.map_screen([upt])[0] if "right" in self.origin: ll_x, ur_x = ur_x, ll_x if "top" in self.origin: ll_y, ur_y = ur_y, ll_y virtual_width = ur_x - ll_x virtual_height = ur_y - ll_y # Convert to the coordinates of the graphics context, which expects # origin to be at the center of a pixel. ll_x += 0.5 ll_y += 0.5 args = self.position \ + self.bounds \ + [ll_x, ll_y, virtual_width, virtual_height] img_pixels, gc_rect = self._calc_zoom_coords(*args) # Grab the appropriate sub-image, if necessary if img_pixels is not None: i1, j1, i2, j2 = img_pixels if "top" in self.origin: y_length = self.value.get_array_bounds()[1][1] j1 = y_length - j1 j2 = y_length - j2 # swap so that j1 < j2 j1, j2 = j2, j1 if "right" in self.origin: x_length = self.value.get_array_bounds()[0][1] i1 = x_length - i1 i2 = x_length - i2 # swap so that i1 < i2 i1, i2 = i2, i1 # Since data is row-major, j1 and j2 go first data = data[j1:j2, i1:i2] if mapper is not None: data = mapper(data) # Furthermore, the data presented to the GraphicsContextArray needs to # be contiguous. If it is not, we need to make a copy. if not data.flags['C_CONTIGUOUS']: data = data.copy() if data.shape[2] == 3: kiva_depth = "rgb24" elif data.shape[2] == 4: kiva_depth = "rgba32" else: raise RuntimeError, "Unknown colormap depth value: %i" \ % data.value_depth self._cached_image = GraphicsContextArray(data, pix_format=kiva_depth) if gc_rect is not None: self._cached_dest_rect = gc_rect else: self._cached_dest_rect = (ll_x, ll_y, virtual_width, virtual_height) self._image_cache_valid = True def _calc_zoom_coords(self, px, py, plot_width, plot_height, ix, iy, image_width, image_height): """ Calculates the coordinates of a zoomed sub-image. Because of floating point limitations, it is not advisable to request a extreme level of zoom, e.g., idx or idy > 10^10. Parameters ---------- px : number X-coordinate of plot pixel bounds py : number Y-coordinate of plot pixel bounds plot_width : number Width of plot pixel bounds plot_height : number Height of plot pixel bounds ix : number X-coordinate of image pixel bounds iy : number Y-coordinate of image pixel bounds image_width : number Width of image pixel bounds image_height : number Height of image pixel bounds Returns ------- ((i1, j1, i2, j2), (x, y, dx, dy)) Lower left and upper right indices of the sub-image to be extracted, and graphics context origin and extents to draw the sub-image into. (None, None) No image extraction is necessary. """ if (image_width < 1.5*plot_width) and (image_height < 1.5*plot_height): return (None, None) if 0 in (plot_width, plot_height, image_width, image_height): return (None, None) # We figure out the subimage coordinates using a two-step process: # 1. convert the plot boundaries from screen space into pixel offsets # in the virtual image # 2. convert the coordinates in the virtual image into indices # into the image data array # 3. from the data array indices, compute the screen coordinates of # the corners of the data array sub-indices # in all the cases below, x1,y1 refers to the lower-left corner, and # x2,y2 refers to the upper-right corner. # 1. screen space -> pixel offsets if self.orientation == "h": x1 = px - ix x2 = (px + plot_width) - ix y1 = py - iy y2 = (py + plot_height) - iy else: x1 = px - ix x2 = (px + plot_height) - ix y1 = py - iy y2 = (py + plot_width) - iy # 2. pixel offsets -> data array indices # X and Y are transposed because for image plot data pixel_bounds = self.value.get_array_bounds() xpixels = pixel_bounds[0][1] - pixel_bounds[0][0] ypixels = pixel_bounds[1][1] - pixel_bounds[1][0] i1 = max(floor(float(x1) / image_width * xpixels), 0) i2 = min(ceil(float(x2) / image_width * xpixels), xpixels) j1 = max(floor(float(y1) / image_height * ypixels), 0) j2 = min(ceil(float(y2) / image_height * ypixels), ypixels) # 3. array indices -> new screen space coordinates x1 = float(i1)/xpixels * image_width + ix x2 = float(i2)/xpixels * image_width + ix y1 = float(j1)/ypixels * image_height + iy y2 = float(j2)/ypixels * image_height + iy # Handle really, really, subpixel cases subimage_index = [i1, j1, i2, j2] subimage_coords = [x1, y1, x2-x1, y2-y1] plot_dimensions = (px, py, plot_width, plot_height) xparams = (0, 2) yparams = (1, 3) for pos_index, size_index in (xparams, yparams): if subimage_index[pos_index] == subimage_index[pos_index+2]-1: # xcoords lie inside the same pixel, so set the subimage # coords to be the width of the image subimage_coords[pos_index] = plot_dimensions[pos_index] subimage_coords[size_index] = plot_dimensions[size_index] elif subimage_index[pos_index] == subimage_index[pos_index+2]-2: # coords span across a pixel boundary. Find the scaling # factor of the virtual (and potentially large) subimage # size to the image size, and scale it down. We can do # this without distortion b/c we are straddling only one # pixel boundary. # # If we scale down the extent to twice the screen size, we can # be sure that no matter what the offset, we will cover the # entire screen, since we are only straddling one pixel boundary. # The formula for calculating the new origin can be worked out # on paper. extent = subimage_coords[size_index] pixel_extent = extent/2 # we are indexed into two pixels origin = subimage_coords[pos_index] scale = float(2 * plot_dimensions[size_index] / extent) subimage_coords[size_index] *= scale subimage_coords[pos_index] = origin + (1-scale)*pixel_extent subimage_index = map(int, subimage_index) return [subimage_index, subimage_coords] #------------------------------------------------------------------------ # Event handlers #------------------------------------------------------------------------ def _index_data_changed_fired(self): self._image_cache_valid = False self.request_redraw() def _index_mapper_changed_fired(self): self._image_cache_valid = False self.request_redraw() def _value_data_changed_fired(self): self._image_cache_valid = False self.request_redraw() chaco-4.5.0/chaco/jitterplot.py0000644000076600000240000003146512426462425017224 0ustar jrocherstaff00000000000000 from __future__ import with_statement from itertools import izip from math import sqrt import numpy as np from enable.api import black_color_trait, MarkerTrait from traits.api import (Any, Bool, Callable, Enum, Float, Instance, Int, Property, Str, Trait, on_trait_change) from abstract_plot_renderer import AbstractPlotRenderer from abstract_mapper import AbstractMapper from array_data_source import ArrayDataSource from base import reverse_map_1d from scatterplot import render_markers class JitterPlot(AbstractPlotRenderer): """A renderer for a jitter plot, a 1D plot with some width in the dimension perpendicular to the primary axis. Useful for understanding dense collections of points. """ # The data source of values index = Instance(ArrayDataSource) # The single mapper that this plot uses mapper = Instance(AbstractMapper) # Just an alias for "mapper" index_mapper = Property(lambda obj,attr: getattr(obj, "mapper"), lambda obj,attr,val: setattr(obj, "mapper", val)) x_mapper = Property() y_mapper = Property() orientation = Enum("h", "v") # The size, in pixels, of the area over which to spread the data points # along the dimension orthogonal to the index direction. jitter_width = Int(50) # How the plot should center itself along the orthogonal dimension if the # component's width is greater than the jitter_width #align = Enum("center", "left", "right", "top", "bottom") # The type of marker to use. This is a mapped trait using strings as the # keys. marker = MarkerTrait # The pixel size of the marker, not including the thickness of the outline. marker_size = Float(4.0) # The CompiledPath to use if **marker** is set to "custom". This attribute # must be a compiled path for the Kiva context onto which this plot will # be rendered. Usually, importing kiva.GraphicsContext will do # the right thing. custom_symbol = Any # The function which actually renders the markers render_markers_func = Callable(render_markers) # The thickness, in pixels, of the outline to draw around the marker. If # this is 0, no outline is drawn. line_width = Float(1.0) # The fill color of the marker. color = black_color_trait # The color of the outline to draw around the marker. outline_color = black_color_trait #------------------------------------------------------------------------ # Built-in selection handling #------------------------------------------------------------------------ # The name of the metadata attribute to look for on the datasource for # determine which points are selected and which are not. The metadata # value returned should be a *list* of numpy arrays suitable for masking # the values returned by index.get_data(). selection_metadata_name = Str("selections") # The color to use to render selected points selected_color = black_color_trait # Alpha value to apply to points that are not in the set of "selected" # points unselected_alpha = Float(0.3) unselected_line_width = Float(0.0) #------------------------------------------------------------------------ # Private traits #------------------------------------------------------------------------ _cache_valid = Bool(False) _cached_data_pts = Any() _cached_data_pts_sorted = Any() _cached_data_argsort = Any() _screen_cache_valid = Bool(False) _cached_screen_pts = Any() _cached_screen_map = Any() # dict mapping index to value points # The random number seed used to generate the jitter. We store this # so that the jittering is stable as the data is replotted. _jitter_seed = Trait(None, None, Int) #------------------------------------------------------------------------ # Component/AbstractPlotRenderer interface #------------------------------------------------------------------------ def map_screen(self, data_array): """ Maps an array of data points into screen space and returns it as an array. Although the orthogonal (non-scaled) axis does not have a mapper, this method returns the scattered values in that dimension. Implements the AbstractPlotRenderer interface. """ if len(data_array) == 0: return np.zeros(0) if self._screen_cache_valid: sm = self._cached_screen_map new_x = [x for x in data_array if x not in sm] if new_x: new_y = self._make_jitter_vals(len(new_x)) sm.update(dict((new_x[i], new_y[i]) for i in range(len(new_x)))) xs = self.mapper.map_screen(data_array) ys = [sm[x] for x in xs] else: if self._jitter_seed is None: self._set_seed(data_array) xs = self.mapper.map_screen(data_array) ys = self._make_jitter_vals(len(data_array)) if self.orientation == "h": return np.vstack((xs, ys)).T else: return np.vstack((ys, xs)).T def _make_jitter_vals(self, numpts): vals = np.random.uniform(0, self.jitter_width, numpts) if self.orientation == "h": ymin = self.y height = self.height vals += ymin + height/2 - self.jitter_width/2 else: xmin = self.x width = self.width vals += xmin + width/2 - self.jitter_width/2 return vals def map_data(self, screen_pt): """ Maps a screen space point into the index space of the plot. """ x, y = screen_pt if self.orientation == "v": x, y = y, x return self.mapper.map_data(x) def map_index(self, screen_pt, threshold=2.0, outside_returns_none=True, \ index_only = True): """ Maps a screen space point to an index into the plot's index array(s). """ screen_points = self._cached_screen_pts if len(screen_points) == 0: return None data_pt = self.map_data(screen_pt) if ((data_pt < self.mapper.range.low) or \ (data_pt > self.mapper.range.high)) and outside_returns_none: return None if self._cached_data_pts_sorted is None: self._cached_data_argsort = np.argsort(self._cached_data_pts) self._cached_data_pts_sorted = self._cached_data_pts[self._cached_data_argsort] data = self._cached_data_pts_sorted try: ndx = reverse_map_1d(data, data_pt, "ascending") except IndexError, e: if outside_returns_none: return None else: if data_pt < data[0]: return 0 else: return len(data) - 1 orig_ndx = self._cached_data_argsort[ndx] if threshold == 0.0: return orig_ndx sx, sy = screen_points[orig_ndx] if sqrt((screen_pt[0] - sx)**2 + (screen_pt[1] - sy)**2) <= threshold: return orig_ndx else: return None def _draw_plot(self, gc, view_bounds=None, mode="normal"): pts = self.get_screen_points() self._render(gc, pts) #------------------------------------------------------------------------ # Private methods #------------------------------------------------------------------------ def get_screen_points(self): if not self._screen_cache_valid: self._gather_points() pts = self.map_screen(self._cached_data_pts) if self.orientation == "h": self._cached_screen_map = dict((x,y) for x,y in izip(pts[:,0], pts[:,1])) else: self._cached_screen_map = dict((y,x) for x,y in izip(pts[:,0], pts[:,1])) self._cached_screen_pts = pts self._screen_cache_valid = True self._cached_data_pts_sorted = None self._cached_data_argsort = None return self._cached_screen_pts def _gather_points(self): if self._cache_valid: return if not self.index: return index, index_mask = self.index.get_data_mask() if len(index) == 0: self._cached_data_pts = [] self._cache_valid = True return # For the jitter plot, we do not mask or compress the data in any # way, because if we do, we have no way of transforming from screen # points back into dataspace. (Tools will be able to find an index # into the screen points array, but won't be able to go from that # back into the original data points array.) #index_range_mask = self.mapper.range.mask_data(index) #self._cached_data_pts = np.compress(index_mask & index_range_mask, index) self._cached_data_pts = index self._cache_valid = True self._cached_screen_pts = None self._screen_cache_valid = False def _render(self, gc, pts): with gc: gc.clip_to_rect(self.x, self.y, self.width, self.height) if not self.index: return name = self.selection_metadata_name md = self.index.metadata if name in md and md[name] is not None and len(md[name]) > 0: # FIXME: when will we ever encounter multiple masks in the list? sel_mask = md[name][0] sel_pts = np.compress(sel_mask, pts, axis=0) unsel_pts = np.compress(~sel_mask, pts, axis=0) color = list(self.color_) color[3] *= self.unselected_alpha outline_color = list(self.outline_color_) outline_color[3] *= self.unselected_alpha if unsel_pts.size > 0: self.render_markers_func(gc, unsel_pts, self.marker, self.marker_size, tuple(color), self.unselected_line_width, tuple(outline_color), self.custom_symbol) if sel_pts.size > 0: self.render_markers_func(gc, sel_pts, self.marker, self.marker_size, self.selected_color_, self.line_width, self.outline_color_, self.custom_symbol) else: self.render_markers_func(gc, pts, self.marker, self.marker_size, self.color_, self.line_width, self.outline_color_, self.custom_symbol) def _set_seed(self, data_array): """ Sets the internal random seed based on some input data """ if isinstance(data_array, np.ndarray): seed = np.random.seed(data_array.size) else: seed = np.random.seed(map(int, data_array[:100])) self._jitter_seed = seed @on_trait_change("index.data_changed") def _invalidate(self): self._cache_valid = False self._screen_cache_valid = False @on_trait_change("mapper.updated") def _invalidate_screen(self): self._screen_cache_valid = False #------------------------------------------------------------------------ # Event handlers #------------------------------------------------------------------------ def _get_x_mapper(self): if self.orientation == "h": return self.mapper else: return None def _set_x_mapper(self, val): if self.orientation == "h": self.mapper = val else: raise ValueError("x_mapper is not defined for a vertical jitter plot") def _get_y_mapper(self): if self.orientation == "v": return self.mapper else: return None def _set_y_mapper(self, val): if self.orientation == "v": self.mapper = val else: raise ValueError("y_mapper is not defined for a horizontal jitter plot") def _update_mappers(self): mapper = self.mapper if mapper is None: return x = self.x x2 = self.x2 y = self.y y2 = self.y2 if "left" in self.origin and self.orientation == 'h': mapper.screen_bounds = (x, x2) elif "right" in self.origin and self.orientation == 'h': mapper.screen_bounds = (x2, x) elif "bottom" in self.origin and self.orientation == 'v': mapper.screen_bounds = (y, y2) elif "top" in self.origin and self.orientation == 'v': mapper.screen_bounds = (y2, y) self.invalidate_draw() self._cache_valid = False self._screen_cache_valid = False def _bounds_changed(self, old, new): super(JitterPlot, self)._bounds_changed(old, new) self._update_mappers() def _bounds_items_changed(self, event): super(JitterPlot, self)._bounds_items_changed(event) self._update_mappers() def _orientation_changed(self): self._update_mappers() chaco-4.5.0/chaco/label.py0000644000076600000240000002525512426452312016075 0ustar jrocherstaff00000000000000""" Defines the Label class. """ from __future__ import with_statement # Major library imports from math import cos, sin, pi from numpy import array, dot # Enthought library imports from enable.api import black_color_trait, transparent_color_trait from kiva.constants import FILL from kiva.trait_defs.kiva_font_trait import KivaFont from traits.api import (Any, Bool, Float, HasTraits, Int, List, Str, on_trait_change) class Label(HasTraits): """ A label used by overlays. Label is not a Component; it's just an object encapsulating text settings and appearance attributes. It can be used by components that need text labels to store state, perform layout, and render the text. """ # The anchor point is the position on the label that is placed at the # label's position. The label is also rotated relative to this point. # "Left" refers to the left edge of the text's bounding box (including # margin), while "center" refers to the horizontal and vertical center # of the bounding box. # TODO: Implement this and test thoroughly #anchor = Enum("left", "right", "top", "bottom", "center", # "top left", "top right", "bottom left", "bottom right") # The label text. Carriage returns (\n) are always connverted into # line breaks. text = Str # The angle of rotation of the label. rotate_angle = Float(0) # The color of the label text. color = black_color_trait # The background color of the label. bgcolor = transparent_color_trait # The width of the label border. If it is 0, then it is not shown. border_width = Int(0) # The color of the border. border_color = black_color_trait # Whether or not the border is visible border_visible = Bool(True) # The font of the label text. font = KivaFont("modern 10") # Number of pixels of margin around the label, for both X and Y dimensions. margin = Int(2) # Number of pixels of spacing between lines of text. line_spacing = Int(5) # Number of pixels to limit the width of the label to. Lines which are # too long will be broken to fit on word boundaries. Line width is # calculated without considering the value of `margin`. # A `max_width` of 0.0 means that lines will not be broken. max_width = Float(0.0) #------------------------------------------------------------------------ # Private traits #------------------------------------------------------------------------ _bounding_box = List() _position_cache_valid = Bool(False) _text_needs_fitting = Bool(False) _line_xpos = Any() _line_ypos = Any() _rot_matrix = Any() def __init__(self, **traits): super(Label, self).__init__(**traits) self._bounding_box = [0, 0] return def get_width_height(self, gc): """ Returns the width and height of the label, in the rotated frame of reference. """ self._fit_text_to_max_width(gc) self._calc_line_positions(gc) width, height = self._bounding_box return width, height def get_bounding_box(self, gc): """ Returns a rectangular bounding box for the Label as (width,height). """ width, height = self.get_width_height(gc) if self.rotate_angle in (90.0, 270.0): return (height, width) elif self.rotate_angle in (0.0, 180.0): return (width, height) else: angle = self.rotate_angle return (abs(width*cos(angle))+abs(height*sin(angle)), abs(height*sin(angle))+abs(width*cos(angle))) def get_bounding_poly(self, gc): """ Returns a list [(x0,y0), (x1,y1),...] of tuples representing a polygon that bounds the label. """ width, height = self.get_width_height(gc) offset = array(self.get_bounding_box(gc))/2. # unrotated points relative to centre base_points = [ array([[-width/2.], [-height/2.]]), array([[-width/2.], [height/2.]]), array([[width/2.], [height/2.]]), array([[width/2.], [-height/2.]]), array([[-width/2.], [-height/2.]]), ] # rotate about centre, and offset to bounding box coords points = [dot(self.get_rotation_matrix(), point).transpose()[0]+offset for point in base_points] return points def get_rotation_matrix(self): return array([[cos(self.rotate_angle), -sin(self.rotate_angle)], [sin(self.rotate_angle), cos(self.rotate_angle)]]) def draw(self, gc): """ Draws the label. This method assumes the graphics context has been translated to the correct position such that the origin is at the lower left-hand corner of this text label's box. """ # Make sure `max_width` is respected self._fit_text_to_max_width(gc) # For this version we're not supporting rotated text. self._calc_line_positions(gc) with gc: bb_width, bb_height = self.get_bounding_box(gc) # Rotate label about center of bounding box width, height = self._bounding_box gc.translate_ctm(bb_width/2.0, bb_height/2.0) gc.rotate_ctm(pi/180.0*self.rotate_angle) gc.translate_ctm(-width/2.0, -height/2.0) # Draw border and fill background if self.bgcolor != "transparent": gc.set_fill_color(self.bgcolor_) gc.draw_rect((0, 0, width, height), FILL) if self.border_visible and self.border_width > 0: gc.set_stroke_color(self.border_color_) gc.set_line_width(self.border_width) border_offset = (self.border_width-1)/2.0 gc.rect(border_offset, border_offset, width-2*border_offset, height-2*border_offset) gc.stroke_path() gc.set_fill_color(self.color_) gc.set_stroke_color(self.color_) gc.set_font(self.font) if self.font.size <= 8.0: gc.set_antialias(0) else: gc.set_antialias(1) lines = self.text.split("\n") if self.border_visible: gc.translate_ctm(self.border_width, self.border_width) width, height = self.get_width_height(gc) for i, line in enumerate(lines): if line == "": continue x_offset = round(self._line_xpos[i]) y_offset = round(self._line_ypos[i]) gc.set_text_position(x_offset, y_offset) gc.show_text(line) #------------------------------------------------------------------------ # Trait handlers #------------------------------------------------------------------------ def _text_changed(self): self._text_needs_fitting = (self.max_width > 0.0) @on_trait_change("font,margin,text,rotate_angle") def _invalidate_position_cache(self): self._position_cache_valid = False #------------------------------------------------------------------------ # Private methods #------------------------------------------------------------------------ def _fit_text_to_max_width(self, gc): """ Break the text into lines whose width is no greater than `max_width`. """ if self._text_needs_fitting: lines = [] with gc: gc.set_font(self.font) for line in self.text.split('\n'): if line == "": lines.append(line) continue width = gc.get_full_text_extent(line)[0] if width > self.max_width: line_words = [] for word in line.split(): line_words.append(word) test_line = ' '.join(line_words) width = gc.get_full_text_extent(test_line)[0] if width > self.max_width: if len(line_words) > 1: lines.append(' '.join(line_words[:-1])) line_words = [word] else: lines.append(word) line_words = [] if len(line_words) > 0: lines.append(' '.join(line_words)) else: lines.append(line) self.trait_setq(text='\n'.join(lines)) self._text_needs_fitting = False def _calc_line_positions(self, gc): if not self._position_cache_valid: with gc: gc.set_font(self.font) # The bottommost line starts at postion (0, 0). x_pos = [] y_pos = [] self._bounding_box = [0, 0] margin = self.margin prev_y_pos = margin prev_y_height = -self.line_spacing max_width = 0 for line in self.text.split("\n")[::-1]: if line != "": (width, height, descent, leading) = \ gc.get_full_text_extent(line) ascent = height - abs(descent) if width > max_width: max_width = width new_y_pos = prev_y_pos + prev_y_height \ + self.line_spacing else: # For blank lines, we use the height of the previous # line, if there is one. The width is 0. leading = 0 if prev_y_height != -self.line_spacing: new_y_pos = prev_y_pos + prev_y_height \ + self.line_spacing ascent = prev_y_height else: new_y_pos = prev_y_pos ascent = 0 x_pos.append(-leading + margin) y_pos.append(new_y_pos) prev_y_pos = new_y_pos prev_y_height = ascent self._line_xpos = x_pos[::-1] self._line_ypos = y_pos[::-1] border_width = self.border_width if self.border_visible else 0 self._bounding_box[0] = max_width + 2*margin + 2*border_width self._bounding_box[1] = prev_y_pos + prev_y_height + margin \ + 2*border_width self._position_cache_valid = True return chaco-4.5.0/chaco/label_axis.py0000644000076600000240000000757012426452312017121 0ustar jrocherstaff00000000000000""" Defines the LabelAxis class. """ # Major library imports from traceback import print_exc from numpy import array, float64, inf, searchsorted, take, unique # Enthought library imports from traits.api import Any, Str, List, Float # Local, relative imports from axis import PlotAxis from label import Label class LabelAxis(PlotAxis): """ An axis whose ticks are labeled with text instead of numbers. """ # List of labels to use on tick marks. labels = List(Str) # The angle of rotation of the label. Only multiples of 90 are supported. label_rotation = Float(0) # List of indices of ticks positions = Any # List(Float), Array def _compute_tick_positions(self, gc, component=None): """ Calculates the positions for the tick marks. Overrides PlotAxis. """ if (self.mapper is None): self._reset_cache() self._cache_valid = True return datalow = self.mapper.range.low datahigh = self.mapper.range.high screenhigh = self.mapper.high_pos screenlow = self.mapper.low_pos if (datalow == datahigh) or (screenlow == screenhigh) or \ (datalow in [inf, -inf]) or (datahigh in [inf, -inf]): self._reset_cache() self._cache_valid = True return if not self.tick_generator: return # Get a set of ticks from the tick generator. tick_list = array(self.tick_generator.get_ticks(datalow, datahigh, datalow, datahigh, self.tick_interval), float64) # Find all the positions in the current range. pos_index = [] pos = [] pos_min = None pos_max = None for i, position in enumerate(self.positions): if datalow <= position <= datahigh: pos_max = max(position, pos_max) if pos_max is not None else position pos_min = min(position, pos_min) if pos_min is not None else position pos_index.append(i) pos.append(position) if len(pos_index) == 0: # No positions currently visible. self._tick_positions = [] self._tick_label_positions = [] self._tick_label_list = [] return # Use the ticks generated by the tick generator as a guide for selecting # the positions to be displayed. tick_indices = unique(searchsorted(pos, tick_list)) tick_indices = tick_indices[tick_indices < len(pos)] tick_positions = take(pos, tick_indices) self._tick_label_list = take(self.labels, take(pos_index, tick_indices)) if datalow > datahigh: raise RuntimeError, "DataRange low is greater than high; unable to compute axis ticks." mapped_label_positions = [((self.mapper.map_screen(pos)-screenlow) / \ (screenhigh-screenlow)) for pos in tick_positions] self._tick_positions = [self._axis_vector*tickpos + self._origin_point \ for tickpos in mapped_label_positions] self._tick_label_positions = self._tick_positions return def _compute_labels(self, gc): """Generates the labels for tick marks. Overrides PlotAxis. """ try: self.ticklabel_cache = [] for text in self._tick_label_list: ticklabel = Label(text=text, font=self.tick_label_font, color=self.tick_label_color, rotate_angle=self.label_rotation) self.ticklabel_cache.append(ticklabel) self._tick_label_bounding_boxes = [array(ticklabel.get_bounding_box(gc), float64) for ticklabel in self.ticklabel_cache] except: print_exc() return chaco-4.5.0/chaco/lasso_overlay.py0000644000076600000240000000571512426452312017677 0ustar jrocherstaff00000000000000""" Defines the LassoOverlay class. """ from __future__ import with_statement from numpy import concatenate, newaxis # Enthought library imports from enable.api import ColorTrait, LineStyle from traits.api import Float, Instance, Bool # Local imports from abstract_overlay import AbstractOverlay class LassoOverlay(AbstractOverlay): """ Draws a lasso selection region on top of a plot. LassoOverlay gets its data from a LassoSelection. """ # The LassoSelection that provides the data for this overlay. lasso_selection = Instance('chaco.tools.lasso_selection.LassoSelection') # The fill color for the selection region. selection_fill_color = ColorTrait('lightskyblue') # The border color for the selection region. selection_border_color = ColorTrait('dodgerblue') # The transparency level for the selection fill color. selection_alpha = Float(0.8) # The width of the selection border. selection_border_width = Float(2.0) # The line style of the selection border. selection_border_dash = LineStyle # The background color (overrides AbstractOverlay). bgcolor = 'clear' # Whether to draw the lasso # depends on the state of the lasso tool _draw_selection = Bool(False) def overlay(self, other_component, gc, view_bounds=None, mode="normal"): """ Draws this component overlaid on another component. Implements AbstractOverlay. """ if not self._draw_selection: return with gc: c = other_component gc.clip_to_rect(c.x, c.y, c.width, c.height) self._draw_component(gc, view_bounds, mode) return def _updated_changed_for_lasso_selection(self): self.component.invalidate_draw() self.component.request_redraw() def _event_state_fired_for_lasso_selection(self, val): self._draw_selection = val == 'selecting' self.component.invalidate_draw() self.component.request_redraw() def _draw_component(self, gc, view_bounds=None, mode='normal'): """ Draws the component. This method is preserved for backwards compatibility with _old_draw(). Overrides PlotComponent. """ with gc: # We may need to make map_screen more flexible in the number of dimensions # it accepts for ths to work well. for selection in self.lasso_selection.disjoint_selections: points = self.component.map_screen(selection) if len(points) == 0: return points = concatenate((points, points[0, newaxis]), axis=0) gc.set_line_width(self.selection_border_width) gc.set_line_dash(self.selection_border_dash_) gc.set_fill_color(self.selection_fill_color_) gc.set_stroke_color(self.selection_border_color_) gc.set_alpha(self.selection_alpha) gc.lines(points) gc.draw_path() chaco-4.5.0/chaco/layers/0000755000076600000240000000000012426466422015741 5ustar jrocherstaff00000000000000chaco-4.5.0/chaco/layers/__init__.py0000644000076600000240000000000012426452312020031 0ustar jrocherstaff00000000000000chaco-4.5.0/chaco/layers/api.py0000644000076600000240000000007712426452312017061 0ustar jrocherstaff00000000000000from status_layer import StatusLayer, ErrorLayer, WarningLayer chaco-4.5.0/chaco/layers/status_layer.py0000644000076600000240000001205312426452312021024 0ustar jrocherstaff00000000000000 from __future__ import with_statement import os.path import xml.etree.cElementTree as etree from chaco.api import AbstractOverlay from pyface.timer.timer import Timer from traits.api import Instance, Str, Enum, Float, Int from enable.savage.svg.document import SVGDocument from enable.savage.svg.backends.kiva.renderer import Renderer as KivaRenderer class StatusLayer(AbstractOverlay): filename = Str() document = Instance(SVGDocument) # Default size attributes if the svg does not specify them doc_width = 48.0 doc_height = 48.0 # The type determines if the layer is displayed as part of the component's # overlay or underlays type = Enum('overlay', 'underlay') # The position of the legend with respect to its overlaid component. # # * c = Center # * ur = Upper Right # * ul = Upper Left # * ll = Lower Left # * lr = Lower Right align = Enum("c", "ur", "ul", "ll", "lr") # How big should the graphic be in comparison to the rest of the plot # area scale_factor = Float(0.5) # Initial transparency alpha = Float(1.0) # The minimum time it takes for the the layer to fade out, in # milliseconds. Actual time may be longer, depending on the pyface toolkit fade_out_time = Float(50) # The number of steps to take to fade from the initial transparency to # invisible fade_out_steps = Int(10) def __init__(self, component, *args, **kw): super(StatusLayer, self).__init__(component, *args, **kw) if self.document is None: if self.filename == '': self.filename = os.path.join(os.path.dirname(__file__), 'data', 'Dialog-error.svg') tree = etree.parse(self.filename) root = tree.getroot() self.document = SVGDocument(root, renderer=KivaRenderer) if hasattr(self.document, 'getSize'): self.doc_width = self.document.getSize()[0] self.doc_height = self.document.getSize()[1] def overlay(self, other_component, gc, view_bounds=None, mode="normal"): """ Draws this component overlaid on another component. Implements AbstractOverlay. """ with gc: gc.set_alpha(self.alpha) plot_width = self.component.width plot_height = self.component.height origin_x = self.component.padding_left origin_y = self.component.padding_top # zoom percentage, use the scale_factor as a % of the plot size. # base the size on the smaller aspect - if the plot is tall and narrow # the overlay should be 50% of the width, if the plot is short and wide # the overlay should be 50% of the height. if gc.height() < gc.width(): scale = (plot_height/self.doc_height)*self.scale_factor else: scale = (plot_width/self.doc_width)*self.scale_factor scale_width = scale*self.doc_width scale_height = scale*self.doc_height # Set up the transforms to align the graphic to the desired position if self.align == 'ur': gc.translate_ctm(origin_x + (plot_width-scale_width), origin_y + plot_height) elif self.align == 'lr': gc.translate_ctm(origin_x + (plot_width-scale_width), origin_y + scale_height) elif self.align == 'ul': gc.translate_ctm(origin_x, origin_y + plot_height) elif self.align == 'll': gc.translate_ctm(origin_x, origin_y + scale_height) else: gc.translate_ctm(origin_x + (plot_width-scale_width)/2, origin_y + (plot_height+scale_height)/2) # SVG origin is the upper right with y positive down, so # we need to flip everything gc.scale_ctm(scale, -scale) self.document.render(gc) self._draw_component(gc, view_bounds, mode) return def fade_out(self): interval = self.fade_out_time/self.fade_out_steps self.timer = Timer(interval, self._fade_out_step) def _fade_out_step(self): """ Fades out the overlay over a half second. then removes it from the other_component's overlays """ if self.alpha <= 0: if self.type == 'overlay': self.component.overlays.remove(self) else: self.component.underlays.remove(self) self.alpha = 1.0 raise StopIteration else: self.alpha -= 0.1 self.component.request_redraw() class ErrorLayer(StatusLayer): filename = os.path.join(os.path.dirname(__file__), 'data', 'Dialog-error.svg') class WarningLayer(StatusLayer): filename = os.path.join(os.path.dirname(__file__), 'data', 'Dialog-warning.svg') chaco-4.5.0/chaco/layers/svg_range_selection_overlay.py0000644000076600000240000001102512426452312024064 0ustar jrocherstaff00000000000000 from __future__ import with_statement import os import numpy from chaco.api import GridMapper from traits.api import Property, Enum, Str, cached_property from status_layer import StatusLayer class SvgRangeSelectionOverlay(StatusLayer): """ This is a primitive range selection overlay which uses a SVG to define the overlay. TODO: not inherit from StatusLayer, this was a convenience for a quick prototype TODO: use 2 svgs, one which defines the border and does not scale, and the other which defines the fill. """ filename = os.path.join(os.path.dirname(__file__), 'data', 'range_selection.svg') alpha = 0.5 # The axis to which this tool is perpendicular. axis = Enum("index", "value") axis_index = Property(depends_on='axis') # Mapping from screen space to data space. By default, it is just # self.component. plot = Property(depends_on='component') # The mapper (and associated range) that drive this RangeSelectionOverlay. # By default, this is the mapper on self.plot that corresponds to self.axis. mapper = Property(depends_on='plot') # The name of the metadata to look at for dataspace bounds. The metadata # can be either a tuple (dataspace_start, dataspace_end) in "selections" or # a boolean array mask of seleted dataspace points with any other name metadata_name = Str("selections") def overlay(self, component, gc, view_bounds=None, mode="normal"): """ Draws this component overlaid on another component. Overrides AbstractOverlay. """ # Draw the selection coords = self._get_selection_screencoords() if len(coords) == 0: return with gc: gc.set_alpha(self.alpha) plot_width = self.component.width plot_height = self.component.height origin_x = self.component.padding_left origin_y = self.component.padding_top if self.axis == 'index': if isinstance(self.mapper, GridMapper): scale_width = (coords[-1][0] - coords[0][0])/self.doc_width else: scale_width = (coords[0][-1] - coords[0][0])/self.doc_width scale_height = float(plot_height)/self.doc_height gc.translate_ctm(coords[0][0], origin_y + plot_height) else: scale_height = (coords[0][-1] - coords[0][0])/self.doc_height scale_width = float(plot_width)/self.doc_width gc.translate_ctm(origin_x, coords[0][0]) # SVG origin is the upper right with y positive down, so # we need to flip everything gc.scale_ctm(scale_width, -scale_height) self.document.render(gc) self._draw_component(gc, view_bounds, mode) return def _get_selection_screencoords(self): """ Returns a tuple of (x1, x2) screen space coordinates of the start and end selection points. If there is no current selection, then returns None. """ ds = getattr(self.plot, self.axis) selection = ds.metadata[self.metadata_name] # "selections" metadata must be a tuple if self.metadata_name == "selections": if selection is not None and len(selection) == 2: return [self.mapper.map_screen(numpy.array(selection))] else: return [] # All other metadata is interpreted as a mask on dataspace else: ar = numpy.arange(0,len(selection), 1) runs = arg_find_runs(ar[selection]) coords = [] for inds in runs: start = ds._data[ar[selection][inds[0]]] end = ds._data[ar[selection][inds[1]-1]] coords.append(self.map_screen(numpy.array((start, end)))) return coords @cached_property def _get_plot(self): return self.component @cached_property def _get_axis_index(self): if self.axis == 'index': return 0 else: return 1 @cached_property def _get_mapper(self): # If the plot's mapper is a GridMapper, return either its # x mapper or y mapper mapper = getattr(self.plot, self.axis + "_mapper") if isinstance(mapper, GridMapper): if self.axis == 'index': return mapper._xmapper else: return mapper._ymapper else: return mapper chaco-4.5.0/chaco/legend.py0000644000076600000240000004701312426452312016250 0ustar jrocherstaff00000000000000""" Defines the Legend, AbstractCompositeIconRenderer, and CompositeIconRenderer classes. """ from __future__ import with_statement from numpy import array, zeros_like from enable.api import black_color_trait, white_color_trait from enable.font_metrics_provider import font_metrics_provider from kiva.trait_defs.kiva_font_trait import KivaFont from traits.api import Any, Dict, Enum, Bool, HasTraits, Int, \ Instance, List, CList, Float, Str # Local relative imports from abstract_overlay import AbstractOverlay from label import Label from lineplot import LinePlot from plot_component import PlotComponent from scatterplot import ScatterPlot class AbstractCompositeIconRenderer(HasTraits): """ Abstract class for an icon renderer. """ def render_icon(self, plots, gc, x, y, width, height): """ Renders an icon representing the given list of plots onto the graphics context, using the given dimensions and at the specified position. """ raise NotImplementedError class CompositeIconRenderer(AbstractCompositeIconRenderer): """ Renderer for composite icons. """ def render_icon(self, plots, *render_args): """ Renders an icon for a list of plots. """ types = set(map(type, plots)) if types == set([ScatterPlot]): self._render_scatterplots(plots, *render_args) elif types == set([LinePlot]): self._render_lineplots(plots, *render_args) elif types == set([ScatterPlot, LinePlot]): self._render_line_scatter(plots, *render_args) else: raise ValueError("Don't know how to render combination plot with " +\ "renderers " + str(types)) return def _render_scatterplots(self, plots, gc, x, y, width, height): # Don't support this for now pass def _render_lineplots(self, plots, gc, x, y, width, height): # Assume they are all the same color/appearance and use the first one plots[0]._render_icon(gc, x, y, width, height) def _render_line_scatter(self, plots, gc, x, y, width, height): # Separate plots into line and scatter renderers; render one of each scatter = [p for p in plots if type(p) == ScatterPlot] line = [p for p in plots if type(p) == LinePlot] line[0]._render_icon(gc, x, y, width, height) scatter[0]._render_icon(gc, x, y, width, height) class Legend(AbstractOverlay): """ A legend for a plot. """ # The font to use for the legend text. font = KivaFont("modern 12") # The amount of space between the content of the legend and the border. border_padding = Int(10) # The border is visible (overrides Enable Component). border_visible = True # The color of the text labels color = black_color_trait # The background color of the legend (overrides AbstractOverlay). bgcolor = white_color_trait # The position of the legend with respect to its overlaid component. (This # attribute applies only if the legend is used as an overlay.) # # * ur = Upper Right # * ul = Upper Left # * ll = Lower Left # * lr = Lower Right align = Enum("ur", "ul", "ll", "lr") # The amount of space between legend items. line_spacing = Int(3) # The size of the icon or marker area drawn next to the label. icon_bounds = List([24, 24]) # Amount of spacing between each label and its icon. icon_spacing = Int(5) # Map of labels (strings) to plot instances or lists of plot instances. The # Legend determines the appropriate rendering of each plot's marker/line. plots = Dict # The list of labels to show and the order to show them in. If this # list is blank, then the keys of self.plots is used and displayed in # alphabetical order. Otherwise, only the items in the **labels** # list are drawn in the legend. Labels are ordered from top to bottom. labels = List # Whether or not to hide plots that are not visible. (This is checked during # layout.) This option *will* filter out the items in **labels** above, so # if you absolutely, positively want to set the items that will always # display in the legend, regardless of anything else, then you should turn # this option off. Otherwise, it usually makes sense that a plot renderer # that is not visible will also not be in the legend. hide_invisible_plots = Bool(True) # If hide_invisible_plots is False, we can still choose to render the names # of invisible plots with an alpha. invisible_plot_alpha = Float(0.33) # The renderer that draws the icons for the legend. composite_icon_renderer = Instance(AbstractCompositeIconRenderer) # Action that the legend takes when it encounters a plot whose icon it # cannot render: # # * 'skip': skip it altogether and don't render its name # * 'blank': render the name but leave the icon blank (color=self.bgcolor) # * 'questionmark': render a "question mark" icon error_icon = Enum("skip", "blank", "questionmark") # Should the legend clip to the bounds it needs, or to its parent? clip_to_component = Bool(False) # The legend is not resizable (overrides PlotComponent). resizable = "hv" # An optional title string to show on the legend. title = Str('') # If True, title is at top, if False then at bottom. title_at_top = Bool(True) # The legend draws itself as in one pass when its parent is drawing # the **draw_layer** (overrides PlotComponent). unified_draw = True # The legend is drawn on the overlay layer of its parent (overrides # PlotComponent). draw_layer = "overlay" #------------------------------------------------------------------------ # Private Traits #------------------------------------------------------------------------ # A cached list of Label instances _cached_labels = List # A cached array of label sizes. _cached_label_sizes = Any # A cached list of label names. _cached_label_names = CList # A list of the visible plots. Each plot corresponds to the label at # the same index in _cached_label_names. This list does not necessarily # correspond to self.plots.value() because it is sorted according to # the plot name and it potentially excludes invisible plots. _cached_visible_plots = CList # A cached array of label positions relative to the legend's origin _cached_label_positions = Any def is_in(self, x, y): """ overloads from parent class because legend alignment and padding does not cooperatate with the basic implementation This may just be caused byt a questionable implementation of the legend tool, but it works by adjusting the padding. The Component class implementation of is_in uses the outer positions which includes the padding """ in_x = (x >= self.x) and (x <= self.x + self.width) in_y = (y >= self.y) and (y <= self.y + self.height) return in_x and in_y def overlay(self, component, gc, view_bounds=None, mode="normal"): """ Draws this component overlaid on another component. Implements AbstractOverlay. """ self.do_layout() valign, halign = self.align if valign == "u": y = component.y2 - self.outer_height else: y = component.y if halign == "r": x = component.x2 - self.outer_width else: x = component.x self.outer_position = [x, y] if self.clip_to_component: c = self.component with gc: gc.clip_to_rect(c.x, c.y, c.width, c.height) PlotComponent._draw(self, gc, view_bounds, mode) else: PlotComponent._draw(self, gc, view_bounds, mode) return # The following two methods implement the functionality of the Legend # to act as a first-class component instead of merely as an overlay. # The make the Legend use the normal PlotComponent render methods when # it does not have a .component attribute, so that it can have its own # overlays (e.g. a PlotLabel). # # The core legend rendering method is named _draw_as_overlay() so that # it can be called from _draw_plot() when the Legend is not an overlay, # and from _draw_overlay() when the Legend is an overlay. def _draw_plot(self, gc, view_bounds=None, mode="normal"): if self.component is None: self._draw_as_overlay(gc, view_bounds, mode) return def _draw_overlay(self, gc, view_bounds=None, mode="normal"): if self.component is not None: self._draw_as_overlay(gc, view_bounds, mode) else: PlotComponent._draw_overlay(self, gc, view_bounds, mode) return def _draw_as_overlay(self, gc, view_bounds=None, mode="normal"): """ Draws the overlay layer of a component. Overrides PlotComponent. """ # Determine the position we are going to draw at from our alignment # corner and the corresponding outer_padding parameters. (Position # refers to the lower-left corner of our border.) # First draw the border, if necesssary. This sort of duplicates # the code in PlotComponent._draw_overlay, which is unfortunate; # on the other hand, overlays of overlays seem like a rather obscure # feature. with gc: gc.clip_to_rect(int(self.x), int(self.y), int(self.width), int(self.height)) edge_space = self.border_width + self.border_padding icon_width, icon_height = self.icon_bounds icon_x = self.x + edge_space text_x = icon_x + icon_width + self.icon_spacing y = self.y2 - edge_space if self._cached_label_positions is not None: if len(self._cached_label_positions) > 0: self._cached_label_positions[:,0] = icon_x for i, label_name in enumerate(self._cached_label_names): # Compute the current label's position label_height = self._cached_label_sizes[i][1] y -= label_height self._cached_label_positions[i][1] = y # Try to render the icon icon_y = y + (label_height - icon_height) / 2 #plots = self.plots[label_name] plots = self._cached_visible_plots[i] render_args = (gc, icon_x, icon_y, icon_width, icon_height) try: if isinstance(plots, list) or isinstance(plots, tuple): # TODO: How do we determine if a *group* of plots is # visible or not? For now, just look at the first one # and assume that applies to all of them if not plots[0].visible: # TODO: the get_alpha() method isn't supported on the Mac kiva backend #old_alpha = gc.get_alpha() old_alpha = 1.0 gc.set_alpha(self.invisible_plot_alpha) else: old_alpha = None if len(plots) == 1: plots[0]._render_icon(*render_args) else: self.composite_icon_renderer.render_icon(plots, *render_args) elif plots is not None: # Single plot if not plots.visible: #old_alpha = gc.get_alpha() old_alpha = 1.0 gc.set_alpha(self.invisible_plot_alpha) else: old_alpha = None plots._render_icon(*render_args) else: old_alpha = None # Or maybe 1.0? icon_drawn = True except: icon_drawn = self._render_error(*render_args) if icon_drawn: # Render the text gc.translate_ctm(text_x, y) gc.set_antialias(0) self._cached_labels[i].draw(gc) gc.set_antialias(1) gc.translate_ctm(-text_x, -y) # Advance y to the next label's baseline y -= self.line_spacing if old_alpha is not None: gc.set_alpha(old_alpha) return def _render_error(self, gc, icon_x, icon_y, icon_width, icon_height): """ Renders an error icon or performs some other action when a plot is unable to render its icon. Returns True if something was actually drawn (and hence the legend needs to advance the line) or False if nothing was drawn. """ if self.error_icon == "skip": return False elif self.error_icon == "blank" or self.error_icon == "questionmark": with gc: gc.set_fill_color(self.bgcolor_) gc.rect(icon_x, icon_y, icon_width, icon_height) gc.fill_path() return True else: return False def get_preferred_size(self): """ Computes the size and position of the legend based on the maximum size of the labels, the alignment, and position of the component to overlay. """ # Gather the names of all the labels we will create if len(self.plots) == 0: return [0, 0] plot_names, visible_plots = map(list, zip(*sorted(self.plots.items()))) label_names = self.labels if len(label_names) == 0: if len(self.plots) > 0: label_names = plot_names else: self._cached_labels = [] self._cached_label_sizes = [] self._cached_label_names = [] self._cached_visible_plots = [] self.outer_bounds = [0, 0] return [0, 0] if self.hide_invisible_plots: visible_labels = [] visible_plots = [] for name in label_names: # If the user set self.labels, there might be a bad value, # so ensure that each name is actually in the plots dict. if name in self.plots: val = self.plots[name] # Rather than checking for a list/TraitListObject/etc., we just check # for the attribute first if hasattr(val, 'visible'): if val.visible: visible_labels.append(name) visible_plots.append(val) else: # If we have a list of renderers, add the name if any of them are # visible for renderer in val: if renderer.visible: visible_labels.append(name) visible_plots.append(val) break label_names = visible_labels # Create the labels labels = [self._create_label(text) for text in label_names] # For the legend title if self.title_at_top: labels.insert(0, self._create_label(self.title)) label_names.insert(0, 'Legend Label') visible_plots.insert(0, None) else: labels.append(self._create_label(self.title)) label_names.append(self.title) visible_plots.append(None) # We need a dummy GC in order to get font metrics dummy_gc = font_metrics_provider() label_sizes = array([label.get_width_height(dummy_gc) for label in labels]) if len(label_sizes) > 0: max_label_width = max(label_sizes[:, 0]) total_label_height = sum(label_sizes[:, 1]) + (len(label_sizes)-1)*self.line_spacing else: max_label_width = 0 total_label_height = 0 legend_width = max_label_width + self.icon_spacing + self.icon_bounds[0] \ + self.hpadding + 2*self.border_padding legend_height = total_label_height + self.vpadding + 2*self.border_padding self._cached_labels = labels self._cached_label_sizes = label_sizes self._cached_label_positions = zeros_like(label_sizes) self._cached_label_names = label_names self._cached_visible_plots = visible_plots if "h" not in self.resizable: legend_width = self.outer_width if "v" not in self.resizable: legend_height = self.outer_height return [legend_width, legend_height] def get_label_at(self, x, y): """ Returns the label object at (x,y) """ for i, pos in enumerate(self._cached_label_positions): size = self._cached_label_sizes[i] corner = pos + size if (pos[0] <= x <= corner[0]) and (pos[1] <= y <= corner[1]): return self._cached_labels[i] else: return None def _do_layout(self): if self.component is not None or len(self._cached_labels) == 0 or \ self._cached_label_sizes is None or len(self._cached_label_names) == 0: width, height = self.get_preferred_size() self.outer_bounds = [width, height] return def _create_label(self, text): """ Returns a new Label instance for the given text. Subclasses can override this method to customize the creation of labels. """ return Label(text=text, font=self.font, margin=0, color=self.color_, bgcolor="transparent", border_width=0) def _composite_icon_renderer_default(self): return CompositeIconRenderer() #-- trait handlers -------------------------------------------------------- def _anytrait_changed(self, name, old, new): if name in ("font", "border_padding", "padding", "line_spacing", "icon_bounds", "icon_spacing", "labels", "plots", "plots_items", "labels_items", "border_width", "align", "position", "position_items", "bounds", "bounds_items", "label_at_top"): self._layout_needed = True if name == "color": self.get_preferred_size() return def _plots_changed(self): """ Invalidate the caches. """ self._cached_labels = [] self._cached_label_sizes = None self._cached_label_names = [] self._cached_visible_plots = [] self._cached_label_positions = None def _title_at_top_changed(self, old, new): """ Trait handler for when self.title_at_top changes. """ if old == True: indx = 0 else: indx = -1 if old != None: self._cached_labels.pop(indx) self._cached_label_names.pop(indx) self._cached_visible_plots.pop(indx) # For the legend title if self.title_at_top: self._cached_labels.insert(0, self._create_label(self.title)) self._cached_label_names.insert(0, '__legend_label__') self._cached_visible_plots.insert(0, None) else: self._cached_labels.append(self._create_label(self.title)) self._cached_label_names.append(self.title) self._cached_visible_plots.append(None) #-- end Legend ---------------------------------------------------------------- chaco-4.5.0/chaco/linear_mapper.py0000644000076600000240000000713312426452312017627 0ustar jrocherstaff00000000000000""" Defines the LinearMapper class, which maps from a 1-D region in data space into a 1-D output space. """ # Major library imports from numpy import array, empty, ndarray # Enthought library imports from traits.api import Bool, Float # Local relative imports from base_1d_mapper import Base1DMapper class LinearMapper(Base1DMapper): """ Maps a 1-D data space to and from screen space by specifying a range in data space and a corresponding fixed line in screen space. This class concerns itself only with metric and not with orientation. So, to "flip" the screen space orientation, simply swap the values for **low_pos** and **high_pos**. """ #------------------------------------------------------------------------ # Private traits #------------------------------------------------------------------------ # Number of screen space units per data space unit. _scale = Float(1.0) # Is the range of the screen space empty? _null_screen_range = Bool(False) # Is the range of the data space empty? _null_data_range = Bool(False) #------------------------------------------------------------------------ # Public methods #------------------------------------------------------------------------ def map_screen(self, data_array): """ map_screen(data_array) -> screen_array Overrides AbstractMapper. Maps values from data space into screen space. """ self._compute_scale() if self._null_data_range: if isinstance(data_array, (tuple, list, ndarray)): x = empty(data_array.shape) x.fill(self.low_pos) return x else: return array([self.low_pos]) else: return (data_array - self.range.low) * self._scale + self.low_pos def map_data(self, screen_val): """ map_data(screen_val) -> data_val Overrides AbstractMapper. Maps values from screen space into data space. """ self._compute_scale() if self._null_screen_range: return array([self.range.low]) elif self._null_data_range: return array([self.range.low]) else: return (screen_val - self.low_pos) / self._scale + self.range.low def map_data_array(self, screen_vals): """ map_data_array(screen_vals) -> data_vals Overrides AbstractMapper. Maps an array of values from screen space into data space. """ return self.map_data(screen_vals) #------------------------------------------------------------------------ # Private methods #------------------------------------------------------------------------ def _compute_scale(self): if self._cache_valid: return if self.range is None: self._cache_valid = False return r = self.range screen_range = self.high_pos - self.low_pos data_range = r.high - r.low if screen_range == 0.0: self._null_screen_range = True else: self._null_screen_range = False if data_range == 0.0: self._null_data_range = True else: self._scale = screen_range / data_range # The screen_range might be small enough that dividing by the # data_range causes it to go to 0. Explicitly call bool because # _scale might also be a numpy scalar and yield another numpy scalar # that the Bool trait rejects. self._null_data_range = bool(self._scale == 0.0) self._cache_valid = True return # EOF chaco-4.5.0/chaco/lineplot.py0000644000076600000240000004511112426452312016635 0ustar jrocherstaff00000000000000""" Defines the LinePlot class. """ from __future__ import with_statement # Standard library imports import warnings # Major library imports from numpy import argsort, array, concatenate, inf, invert, isnan, \ take, transpose, zeros, sqrt, argmin, clip, column_stack # Enthought library imports from enable.api import black_color_trait, ColorTrait, LineStyle from traits.api import Enum, Float, List, Str, Property, Tuple, cached_property from traitsui.api import Item, View # Local relative imports from base import arg_find_runs, bin_search, reverse_map_1d from base_xy_plot import BaseXYPlot class LinePlot(BaseXYPlot): """ A plot consisting of a line. This is the most fundamental object to use to create line plots. However, it is somewhat low-level and therefore creating one properly to do what you want can require some verbose code. The create_line_plot() function in plot_factory.py can hide some of this verbosity for common cases. """ # The color of the line. color = black_color_trait # The RGBA tuple for rendering lines. It is always a tuple of length 4. # It has the same RGB values as color_, and its alpha value is the alpha # value of self.color multiplied by self.alpha. effective_color = Property(Tuple, depends_on=['color', 'alpha']) # The color to use to highlight the line when selected. selected_color = ColorTrait("lightyellow") # The style of the selected line. selected_line_style = LineStyle("solid") # The name of the key in self.metadata that holds the selection mask metadata_name = Str("selections") # The thickness of the line. line_width = Float(1.0) # The line dash style. line_style = LineStyle # The rendering style of the line plot. # # connectedpoints # "normal" style (default); each point is connected to subsequent and # prior points by line segments # hold # each point is represented by a line segment parallel to the abscissa # (index axis) and spanning the length between the point and its # subsequent point. # connectedhold # like "hold" style, but line segments are drawn at each point of the # plot to connect the hold lines of the prior point and the current # point. Also called a "right angle plot". render_style = Enum("connectedpoints", "hold", "connectedhold") # Traits UI View for customizing the plot. traits_view = View(Item("color", style="custom"), "line_width", "line_style", buttons=["OK", "Cancel"]) #------------------------------------------------------------------------ # Private traits #------------------------------------------------------------------------ # Cached list of non-NaN arrays of (x,y) data-space points; regardless of # self.orientation, this is always stored as (index_pt, value_pt). This is # different from the default BaseXYPlot definition. _cached_data_pts = List # Cached list of non-NaN arrays of (x,y) screen-space points. _cached_screen_pts = List def hittest(self, screen_pt, threshold=7.0, return_distance = False): """ Tests whether the given screen point is within *threshold* pixels of any data points on the line. If so, then it returns the (x,y) value of a data point near the screen point. If not, then it returns None. """ # First, check screen_pt is directly on a point in the lineplot ndx = self.map_index(screen_pt, threshold) if ndx is not None: # screen_pt is one of the points in the lineplot data_pt = (self.index.get_data()[ndx], self.value.get_data()[ndx]) if return_distance: scrn_pt = self.map_screen(data_pt) dist = sqrt((screen_pt[0] - scrn_pt[0])**2 + (screen_pt[1] - scrn_pt[1])**2) return (data_pt[0], data_pt[1], dist) else: return data_pt else: # We now must check the lines themselves # Must check all lines within threshold along the major axis, # so determine the bounds of the region of interest in dataspace if self.orientation == "h": dmax = self.map_data((screen_pt[0]+threshold, screen_pt[1])) dmin = self.map_data((screen_pt[0]-threshold, screen_pt[1])) else: dmax = self.map_data((screen_pt[0], screen_pt[1]+threshold)) dmin = self.map_data((screen_pt[0], screen_pt[1]-threshold)) xmin, xmax = self.index.get_bounds() # Now compute the bounds of the region of interest as indexes if dmin < xmin: ndx1 = 0 elif dmin > xmax: ndx1 = len(self.value.get_data())-1 else: ndx1 = reverse_map_1d(self.index.get_data(), dmin, self.index.sort_order) if dmax < xmin: ndx2 = 0 elif dmax > xmax: ndx2 = len(self.value.get_data())-1 else: ndx2 = reverse_map_1d(self.index.get_data(), dmax, self.index.sort_order) start_ndx = max(0, min(ndx1-1, ndx2-1,)) end_ndx = min(len(self.value.get_data())-1, max(ndx1+1, ndx2+1)) # Compute the distances to all points in the range of interest start = array([ self.index.get_data()[start_ndx:end_ndx], self.value.get_data()[start_ndx:end_ndx] ]) end = array([ self.index.get_data()[start_ndx+1:end_ndx+1], self.value.get_data()[start_ndx+1:end_ndx+1] ]) # Convert to screen points s_start = transpose(self.map_screen(transpose(start))) s_end = transpose(self.map_screen(transpose(end))) # t gives the parameter of the closest point to screen_pt # on the line going from s_start to s_end t = _closest_point(screen_pt, s_start, s_end) # Restrict to points on the line segment s_start->s_end t = clip(t, 0, 1) # Gives the corresponding point on the line px, py = _t_to_point(t, s_start, s_end) # Calculate distances dist = sqrt((px - screen_pt[0])**2 + (py - screen_pt[1])**2) # Find the minimum n = argmin(dist) # And return if it is good if dist[n] <= threshold: best_pt = self.map_data((px[n], py[n]), all_values=True) if return_distance: return [best_pt[0], best_pt[1], dist[n]] else: return best_pt return None def interpolate(self, index_value): """ Returns the value of the plot at the given index value in screen space. Raises an IndexError when *index_value* exceeds the bounds of indexes on the value. """ if self.index is None or self.value is None: raise IndexError, "cannot index when data source index or value is None" index_data = self.index.get_data() value_data = self.value.get_data() ndx = reverse_map_1d(index_data, index_value, self.index.sort_order) # quick test to see if this value is already in the index array if index_value == index_data[ndx]: return value_data[ndx] # get x and y values to interpolate between if index_value < index_data[ndx]: x0 = index_data[ndx - 1] y0 = value_data[ndx - 1] x1 = index_data[ndx] y1 = value_data[ndx] else: x0 = index_data[ndx] y0 = value_data[ndx] x1 = index_data[ndx + 1] y1 = value_data[ndx + 1] if x1 != x0: slope = float(y1 - y0)/float(x1 - x0) dx = index_value - x0 yp = y0 + slope * dx else: yp = inf return yp def get_screen_points(self): self._gather_points() return [self.map_screen(ary) for ary in self._cached_data_pts] #------------------------------------------------------------------------ # Private methods; implements the BaseXYPlot stub methods #------------------------------------------------------------------------ def _gather_points(self): """ Collects the data points that are within the bounds of the plot and caches them. """ if not self._cache_valid: if not self.index or not self.value: return index = self.index.get_data() value = self.value.get_data() # Check to see if the data is completely outside the view region for ds, rng in ((self.index, self.index_range), (self.value, self.value_range)): low, high = ds.get_bounds() if low > rng.high or high < rng.low: self._cached_data_pts = [] self._cached_valid = True return if len(index) == 0 or len(value) == 0 or len(index) != len(value): self._cached_data_pts = [] self._cache_valid = True size_diff = len(value) - len(index) if size_diff > 0: warnings.warn('Chaco.LinePlot: len(value) %d - len(index) %d = %d\n' \ % (len(value), len(index), size_diff)) index_max = len(index) value = value[:index_max] else: index_max = len(value) index = index[:index_max] # TODO: restore the functionality of rendering highlighted portions # of the line #selection = self.index.metadata.get(self.metadata_name, None) #if selection is not None and type(selection) in (ndarray, list) and \ # len(selection) > 0: # Split the index and value raw data into non-NaN chunks nan_mask = invert(isnan(value)) & invert(isnan(index)) blocks = [b for b in arg_find_runs(nan_mask, "flat") if nan_mask[b[0]] != 0] points = [] for block in blocks: start, end = block block_index = index[start:end] block_value = value[start:end] index_mask = self.index_mapper.range.mask_data(block_index) runs = [r for r in arg_find_runs(index_mask, "flat") \ if index_mask[r[0]] != 0] # Check to see if our data view region is between two points in the # index data. If so, then we have to reverse map our current view # into the appropriate index and draw the bracketing points. if runs == []: data_pt = self.map_data((self.x_mapper.low_pos, self.y_mapper.low_pos)) if self.index.sort_order == "none": indices = argsort(index) sorted_index = take(index, indices) sorted_value = take(value, indices) sort = 1 else: sorted_index = index sorted_value = value if self.index.sort_order == "ascending": sort = 1 else: sort = -1 ndx = bin_search(sorted_index, data_pt, sort) if ndx == -1: # bin_search can return -1 if data_pt is outside the bounds # of the source data continue points.append(transpose(array((sorted_index[ndx:ndx+2], sorted_value[ndx:ndx+2])))) else: # Expand the width of every group of points so we draw the lines # up to their next point, outside the plot area data_end = len(index_mask) for run in runs: start, end = run if start != 0: start -= 1 if end != data_end: end += 1 run_data = ( block_index[start:end], block_value[start:end] ) run_data = column_stack(run_data) points.append(run_data) self._cached_data_pts = points self._cache_valid = True return def _downsample(self): if not self._screen_cache_valid: self._cached_screen_pts = [self.map_screen(p) for p in self._cached_data_pts] self._screen_cache_valid = True pt_arrays = self._cached_screen_pts # some boneheaded short-circuits m = self.index_mapper total_numpoints = sum([p.shape for p in pt_arrays]) if (total_numpoints < 400) or (total_numpoints < m.high_pos - m.low_pos): return self._cached_screen_pts # the new point array and a counter of how many actual points we've added # to it new_arrays = [] for pts in pt_arrays: new_pts = zeros(pts.shape, "d") numpoints = 1 new_pts[0] = pts[0] last_x, last_y = pts[0] for x, y in pts[1:]: if (x-last_x)**2 + (y-last_y)**2 > 2: new_pts[numpoints] = (x,y) last_x = x last_y = y numpoints += 1 new_arrays.append(new_pts[:numpoints]) return self._cached_screen_pts def _render(self, gc, points, selected_points=None): if len(points) == 0: return with gc: gc.set_antialias(True) gc.clip_to_rect(self.x, self.y, self.width, self.height) render_method_dict = { "hold": self._render_hold, "connectedhold": self._render_connected_hold, "connectedpoints": self._render_normal } render = render_method_dict.get(self.render_style, self._render_normal) if selected_points is not None: gc.set_stroke_color(self.selected_color_) gc.set_line_width(self.line_width+10.0) gc.set_line_dash(self.selected_line_style_) render(gc, selected_points, self.orientation) # Render using the normal style gc.set_stroke_color(self.effective_color) gc.set_line_width(self.line_width) gc.set_line_dash(self.line_style_) render(gc, points, self.orientation) # Draw the default axes, if necessary self._draw_default_axes(gc) @classmethod def _render_normal(cls, gc, points, orientation): for ary in points: if len(ary) > 0: gc.begin_path() gc.lines(ary) gc.stroke_path() return @classmethod def _render_hold(cls, gc, points, orientation): for starts in points: x,y = starts.T if orientation == "h": ends = transpose(array( (x[1:], y[:-1]) )) else: ends = transpose(array( (x[:-1], y[1:]) )) gc.begin_path() gc.line_set(starts[:-1], ends) gc.stroke_path() return @classmethod def _render_connected_hold(cls, gc, points, orientation): for starts in points: x,y = starts.T if orientation == "h": ends = transpose(array( (x[1:], y[:-1]) )) else: ends = transpose(array( (x[:-1], y[1:]) )) gc.begin_path() gc.line_set(starts[:-1], ends) gc.line_set(ends, starts[1:]) gc.stroke_path() return def _render_icon(self, gc, x, y, width, height): with gc: gc.set_stroke_color(self.effective_color) gc.set_line_width(self.line_width) gc.set_line_dash(self.line_style_) gc.set_antialias(0) gc.move_to(x, y+height/2) gc.line_to(x+width, y+height/2) gc.stroke_path() return def _downsample_vectorized(self): """ Analyzes the screen-space points stored in self._cached_data_pts and replaces them with a downsampled set. """ pts = self._cached_screen_pts #.astype(int) # some boneheaded short-circuits m = self.index_mapper if (pts.shape[0] < 400) or (pts.shape[0] < m.high_pos - m.low_pos): return pts2 = concatenate((array([[0.0,0.0]]), pts[:-1])) z = abs(pts - pts2) d = z[:,0] + z[:,1] #... TODO ... return def _alpha_changed(self): self.invalidate_draw() self.request_redraw() return def _color_changed(self): self.invalidate_draw() self.request_redraw() return def _line_style_changed(self): self.invalidate_draw() self.request_redraw() return def _line_width_changed(self): self.invalidate_draw() self.request_redraw() return def __getstate__(self): state = super(LinePlot,self).__getstate__() for key in ['traits_view']: if state.has_key(key): del state[key] return state @cached_property def _get_effective_color(self): alpha = self.color_[-1] if len(self.color_) == 4 else 1 c = self.color_[:3] + (alpha * self.alpha,) return c def _closest_point(target, p1, p2): '''Utility function for hittest: finds the point on the line between p1 and p2 to the target. Returns the 't' value of that point where the line is parametrized as t -> p1*(1-t) + p2*t Notably, if t=0 is p1, t=2 is p2 and anything outside that range is a point outisde p1, p2 on the line Note: can divide by zero, so user should check for that''' t = ((p1[0] - target[0])*(p1[0]-p2[0]) \ + (p1[1] - target[1])*(p1[1]-p2[1]))\ / ((p1[0] - p2[0])*(p1[0] - p2[0]) + (p1[1] - p2[1])*(p1[1] - p2[1])) return t def _t_to_point(t, p1, p2): '''utility function for hittest for use with _closest_point returns the point corresponding to the parameter t on the line going between p1 and p2''' return ( p1[0]*(1-t) + p2[0]*t, p1[1]*(1-t) + p2[1]*t ) # EOF chaco-4.5.0/chaco/log_mapper.py0000644000076600000240000001134212426452312017133 0ustar jrocherstaff00000000000000""" Defines the LogMapper and InvalidDataRangeException classes. """ # Major library imports from numpy import array, isnan, log, log10, exp, zeros, sometrue,\ floor, ceil, ndarray # Enthought library imports from traits.api import Bool, Float #Local relative imports from base_1d_mapper import Base1DMapper LOG_MINIMUM = 0.0 class InvalidDataRangeException(Exception): pass class LogMapper(Base1DMapper): """ Defines a 1-D logarithmic scale mapping from a 1-D region in input space to a 1-D region in output space. """ # The value to map when asked to map values <= LOG_MINIMUM to screen space. fill_value = Float(1.0) #------------------------------------------------------------------------ # Private traits #------------------------------------------------------------------------ _inter_scale = Float(0.0) _inter_offset = Float(0.0) _screen_scale = Float(0.0) _screen_offset = Float(0.0) _null_screen_range = Bool(False) _null_data_range = Bool(False) #------------------------------------------------------------------------ # Public methods #------------------------------------------------------------------------ def map_screen(self, data_array): """ map_screen(data_array) -> screen_array Overrides AbstractMapper. Maps values from data space to screen space. """ # Ensure that data_array is actually an array. if not isinstance(data_array, ndarray): data_array = array(data_array, ndmin=1) # First convert to a [0,1] space, then to the screen space. if not self._cache_valid: self._compute_scale() if self._inter_scale == 0.0: intermediate = data_array*0.0 else: try: mask = (data_array <= LOG_MINIMUM) | isnan(data_array) if sometrue(mask): data_array = array(data_array, copy=True, ndmin=1) data_array[mask] = self.fill_value intermediate = (log(data_array) - self._inter_offset)/self._inter_scale except ValueError: intermediate = zeros(len(data_array)) result = intermediate * self._screen_scale + self._screen_offset return result def map_data(self, screen_val): """ map_data(screen_val) -> data_val Overrides Abstract Mapper. Maps values from screen space into data space. """ if not self._cache_valid: self._compute_scale() if self._null_screen_range or self._null_data_range: return array([self.range.low]) #First convert to a [0,1] space, then to the data space intermediate = (screen_val-self._screen_offset)/self._screen_scale return exp(self._inter_scale*intermediate + self._inter_offset) def map_data_array(self, screen_vals): return self.map_data(screen_vals) #------------------------------------------------------------------------ # Private methods #------------------------------------------------------------------------ def _get_safe_scale(self, range): orig_low = range.low orig_high = range.high if orig_low < LOG_MINIMUM: low = LOG_MINIMUM else: low = orig_low if orig_high < LOG_MINIMUM: high = LOG_MINIMUM else: high = orig_high if low == high: if low == LOG_MINIMUM: low = 1.0 high = 10.0 else: log_val = log10(low) low = pow(10, floor(log_val)) if ceil(log_val) != floor(log_val): high = pow(10, ceil(log_val)) else: high = pow(10, ceil(log_val) + 1) return (low, high) def _compute_scale(self): if self._cache_valid: return if self.range is None: self._cache_valid = False return screen_range = self.high_pos - self.low_pos if screen_range == 0.0: self._null_screen_range = True # Get dataspace low and high from the range that are "safe" for a # logarithmic mapper, i.e. constrained to be between LOG_MINIMUM and inf. low, high = self._get_safe_scale(self.range) if high - low == 0: self._null_data_range = True else: if low == LOG_MINIMUM: self._inter_scale = log(high) self._inter_offset = 0.0 else: self._inter_scale = log(high)-log(low) self._inter_offset = log(low) self._screen_scale = screen_range self._screen_offset = self.low_pos self._cache_valid = True return # EOF chaco-4.5.0/chaco/multi_array_data_source.py0000644000076600000240000001625012426452312021712 0ustar jrocherstaff00000000000000""" Defines the MultiArrayDataSource class. """ # Major package imports from numpy import nanmax, nanmin, array, shape, ones, bool, newaxis, nan_to_num import types # Enthought library imports from traits.api import Any, Int, Tuple # Chaco imports from base import NumericalSequenceTrait, SortOrderTrait from abstract_data_source import AbstractDataSource class MultiArrayDataSource(AbstractDataSource): """ A data source representing a single, continuous array of multidimensional numerical data. It is useful, for example, to define 2D vector data at each point of a scatter plot (as in QuiverPlot), or to represent multiple values for each index (as in MultiLinePlot). This class does not listen to the array for value changes; To implement such behavior, define a subclass that hooks up the appropriate listeners. """ #------------------------------------------------------------------------ # AbstractDataSource traits #------------------------------------------------------------------------ # The dimensionality of the indices into this data source (overrides # AbstractDataSource). index_dimension = Int(0) # The dimensionality of the value at each index point (overrides # AbstractDataSource). value_dimension = Int(1) # The sort order of the data. # This is a specialized optimization for 1-D arrays, but it's an important # one that's used everywhere. sort_order = SortOrderTrait #------------------------------------------------------------------------ # Private traits #------------------------------------------------------------------------ # The data array itself. _data = NumericalSequenceTrait # Cached values of min and max as long as **_data** doesn't change. _cached_bounds = Tuple # Not necessary, since this is not a filter, but provided for convenience. _cached_mask = Any # The index of the (first) minimum value in self._data. _min_index = Int # The index of the (first) maximum value in self._data. _max_index = Int def __init__(self, data=array([]), sort_order="ascending", **traits): super(MultiArrayDataSource, self).__init__(**traits) self._set_data(data) self.sort_order = sort_order #self._compute_bounds() self.data_changed = True return def get_data(self, axes = None, remove_nans=False): """get_data() -> data_array If called with no arguments, this method returns a data array. Treat this data array as read-only, and do not alter it in-place. This data is contiguous and not masked. If *axes* is an integer or tuple, this method returns the data array, sliced along the **index_dimension**. """ if type(axes) == types.IntType: if self.index_dimension == 0: data = self._data[::, axes] else: data = self._data[axes, ::] elif axes is None: data = self._data # fixme: we need to handle the multi-dimensional case. if remove_nans: return nan_to_num(data) else: return data def get_data_mask(self): """get_data_mask() -> (data_array, mask_array) Implements AbstractDataSource. """ if self._cached_mask is None: self._cached_mask = ones(shape(self._data), bool) return self._data, self._cached_mask def is_masked(self): """is_masked() -> bool Returns True if this data source's data uses a mask. In this case, retrieve the data using get_data_mask() instead of get_data(). If you call get_data() for this data source, it returns data, but that data may not be the expected data.) """ return False def get_size(self): """get_size() -> int Implements AbstractDataSource. Returns an integer estimate, or the exact size, of the dataset that get_data() returns. This method is useful for downsampling. """ # return the size along the index dimension size = 0 if self._data is not None: size = shape(self._data)[self.index_dimension] return size def get_value_size(self): """ get_value_size() -> size Returns the size along the value dimension. """ size = 0 if self._data is not None: size = shape(self._data)[self.value_dimension] return size def get_bounds(self, value = None, index = None): """get_bounds() -> tuple(min, max) Returns a tuple (min, max) of the bounding values for the data source. In the case of 2-D data, min and max are 2-D points that represent the bounding corners of a rectangle enclosing the data set. Note that these values are not view-dependent, but represent intrinsic properties of the data source. If data is the empty set, then the min and max vals are 0.0. If *value* and *index* are both None, then the method returns the global minimum and maximum for the entire data set. If *value* is an integer, then the method returns the minimum and maximum along the *value* slice in the **value_dimension**. If *index* is an integer, then the method returns the minimum and maximum along the *index* slice in the **index_direction**. """ if self._data is None or 0 in self._data.shape: return (0.0, 0.0) if type(value) == types.IntType: if self.value_dimension == 0: maxi = nanmax(self._data[value, ::]) mini = nanmin(self._data[value, ::]) else: # value_dimension == 1 maxi = nanmax(self._data[::, value]) mini = nanmin(self._data[::, value]) elif type(index) == types.IntType: if self.index_dimension == 0: maxi = nanmax(self._data[index, ::]) mini = nanmin(self._data[index, ::]) else: # index_dimension == 1 maxi = nanmax(self._data[::, index]) mini = nanmin(self._data[::, index]) else: # value is None and index is None: maxi = nanmax(self._data) mini = nanmin(self._data) return (mini, maxi) def get_shape(self): """ Returns the shape of the multi-dimensional data source. """ return shape(self._data) def set_data(self, value): """ Sets the data for this data source. Parameters ---------- value : array The data to use. """ self._set_data(value) self.data_changed = True return def _set_data(self, value): """ Forces 1-D data to 2-D. """ if len(value.shape) == 1: if self.index_dimension == 0: value = value[:,newaxis] else: value = value[newaxis,:] if len(value.shape) != 2: msg = 'Input is %d dimensional, but it must be 1 or 2' \ 'dimensional.' % len(value.shape) raise ValueError, msg self._data = value # EOF chaco-4.5.0/chaco/multi_line_plot.py0000644000076600000240000004040012426462254020210 0ustar jrocherstaff00000000000000""" Defines the MultiLinePlot class. """ from __future__ import with_statement # Standard library imports import warnings from math import ceil, floor # Major library imports import numpy as np from numpy import argsort, array, invert, isnan, take, transpose # Enthought library imports from enable.api import black_color_trait, ColorTrait, LineStyle from traits.api import Float, List, Str, Trait, \ Bool, Callable, Property, cached_property, Instance, Array from traitsui.api import Item, View, ScrubberEditor, HGroup from array_data_source import ArrayDataSource from base import arg_find_runs, bin_search from base_xy_plot import BaseXYPlot class MultiLinePlot(BaseXYPlot): """ A plot consisting of multiple lines. The data to be plotted must come from a two-dimensional array with shape M by N stored in a MultiArrayDataSource object. M is the number of lines to be plotted, and N is the number of points in each line. Constructor Parameters ---------------------- index : instance of an ArrayDataSource These are the 'x' or abscissa coordinates. yindex : instance of ArrayDataSource These are the 'y' coordinates. value : instance of a MultiArrayDataSource Note that the `scale`, `offset` and `normalized_amplitude` attributes of the MultiLinePlot control the projection of the traces into the (x,y) plot. In simplest case, `scale=1` and `offset=0`, and `normalized_amplitude` controls the scaling of the traces relative to their base y value. global_min, global_max : float The minimum and maximum values of the data in `value`. For large arrays, computing these could take excessive time, so they must be provided when an instance is created. normalized_amplitude : Float color : ColorTrait color_func : Callable or None If not None, this Callable overrides `color`. The argument to `color_func` will be the integer index of the trace to be rendered. `color_func` must return an RGBA 4-tuple. Default: None orientation : str Must be 'v' or 'h' (for 'vertical' or 'horizontal', respectively). This is the orientation of the index axis (i.e. the 'x' axis). Default: 'h' fast_clip : bool If True, traces whose *base* 'y' coordinate is outside the value axis range are not plotted, even if some of the data in the curve extends into the plot region. Default: False line_width : float Width of the plotted lines. line_style : The style of the trace lines in the plot. The following are from the original LinePlot code, and are untested: selected_color selected_line_style """ # M and N appearing in the comments are as defined in the docstring. yindex = Instance(ArrayDataSource) # amplitude = Float(0.0) # `scale` and `offset` provide a more general transformation, but are currently # untested. scale = Float(1.0) offset = Float(0.0) fast_clip = Bool(False) # The color of the lines. color = black_color_trait # A function that returns the color of lines. Overrides `color` if not None. color_func = Trait(None, None, Callable) # The color to use to highlight the line when selected. selected_color = ColorTrait("lightyellow") # The style of the selected line. selected_line_style = LineStyle("solid") # The name of the key in self.metadata that holds the selection mask metadata_name = Str("selections") # The thickness of the line. line_width = Float(1.0) # The line dash style. line_style = LineStyle use_global_bounds = Bool(True) # Minimum value in the `value` data source. This must be provided # in the call to the constructor. global_min = Float # Maximum value in the `value` data source. This must be provided # in the call to the constructor. global_max = Float # Normalized amplitude is the value exposed to the user. normalized_amplitude = Float(-0.5) amplitude_scale = Property(Float, depends_on=['global_min', 'global_max', 'data', 'use_global_bounds', 'yindex']) amplitude = Property(Float, depends_on=['normalized_amplitude', 'amplitude_scale']) #------------------------------------------------------------------------ # Private traits #------------------------------------------------------------------------ # The projected 2D numpy array. _trace_data = Property(Array, depends_on=['index', 'index.data_changed', 'value', 'value.data_changed', 'yindex', 'yindex.data_changed', 'amplitude', 'scale', 'offset']) # Cached list of non-NaN arrays of (x,y) data-space points; regardless of # self.orientation, this is always stored as (index_pt, value_pt). This is # different from the default BaseXYPlot definition. _cached_data_pts = List # Cached list of non-NaN arrays of (x,y) screen-space points. _cached_screen_pts = List #------------------------------------------------------------------------ # #------------------------------------------------------------------------ def trait_view(self, obj): """Create a minimalist View, with just the amplitude and color attributes.""" # Minimalist Traits UI View for customizing the plot: only the trace amplitude # and line color are exposed. view = View( HGroup( Item('use_global_bounds'), # Item('normalized_amplitude'), # Item('normalized_amplitude', editor=RangeEditor()), Item('normalized_amplitude', editor=ScrubberEditor(increment=0.2, hover_color=0xFFFFFF, active_color=0xA0CD9E, border_color=0x0000FF)), ), Item("color", label="Trace color", style="simple"), width=480, title="Trace Plot Line Attributes", buttons=["OK", "Cancel"]) return view #------------------------------------------------------------------------ # #------------------------------------------------------------------------ # See base_xy_plot.py for these: ## def hittest(self, screen_pt, threshold=7.0): ## def interpolate(self, index_value): def get_screen_points(self): self._gather_points() scrn_pts_list = [[self.map_screen(ary) for ary in line] for line in self._cached_data_pts] return scrn_pts_list #------------------------------------------------------------------------ # Private methods #------------------------------------------------------------------------ @cached_property def _get_amplitude_scale(self): """ If the amplitude is set to this value, the largest trace deviation from its base y coordinate will be equal to the y coordinate spacing. """ # Note: Like the rest of the current code, this ignores the `scale` attribute. if self.yindex is not None: coordinates = self.yindex.get_data() else: coordinates = [] if len(coordinates) > 1: dy = coordinates[1] - coordinates[0] if dy == 0: dy = 1.0 else: # default coordinate spacing if there is only 1 coordinate dy = 1.0 if self.use_global_bounds: max_abs = max(abs(self.global_min), abs(self.global_max)) else: data = self.value._data max_abs = np.max(np.abs(data)) if max_abs == 0: amp_scale = 0.5 * dy else: amp_scale = 0.5 * dy / max_abs return amp_scale @cached_property def _get_amplitude(self): amplitude = self.normalized_amplitude * self.amplitude_scale return amplitude @cached_property def _get__trace_data(self): """Compute the transformed data.""" # Get the array from `value` data = self.value._data coordinates = self.yindex.get_data() channel_data = self.scale*(self.amplitude*data + coordinates[:,np.newaxis]) \ + self.offset return channel_data def _gather_points(self): """ Collects the data points that are within the bounds of the plot and caches them. """ if self._cache_valid: return if not self.index or not self.value: return index = self.index.get_data() varray = self._trace_data if varray.size == 0: self._cached_data_pts = [] self._cached_valid = True return coordinates = self.yindex.get_data() if self.fast_clip: coord_min = float(coordinates[0]) coord_max = coordinates[-1] slice_min = max(0,ceil((varray.shape[0]-1)*(self.value_range.low - coord_min)/(coord_max - coord_min))) slice_max = min(varray.shape[0], 1+floor((varray.shape[0]-1)*(self.value_range.high - coord_min)/(coord_max - coord_min))) varray = varray[slice_min:slice_max] # FIXME: The y coordinates must also be sliced to match varray. # Check to see if the data is completely outside the view region. outside = False # Check x coordinates. low, high = self.index.get_bounds() if low > self.index_range.high or high < self.index_range.low: outside = True # Check y coordinates. Use varray because it is nased on the yindex, # but has been shifted up or down depending on the values. ylow, yhigh = varray.min(), varray.max() if ylow > self.value_range.high or yhigh < self.value_range.low: outside = True if outside: self._cached_data_pts = [] self._cached_valid = True return if len(index) == 0 or varray.shape[0] == 0 or varray.shape[1] == 0 \ or len(index) != varray.shape[1]: self._cached_data_pts = [] self._cache_valid = True return size_diff = varray.shape[1] - len(index) if size_diff > 0: warnings.warn('Chaco.LinePlot: value.shape[1] %d - len(index) %d = %d\n' \ % (varray.shape[1], len(index), size_diff)) index_max = len(index) varray = varray[:,:index_max] else: index_max = varray.shape[1] index = index[:index_max] # Split the index and value raw data into non-NaN chunks. # nan_mask is a boolean M by N array. nan_mask = invert(isnan(varray)) & invert(isnan(index)) blocks_list = [] for nm in nan_mask: blocks = [b for b in arg_find_runs(nm, "flat") if nm[b[0]] != 0] blocks_list.append(blocks) line_points = [] for k, blocks in enumerate(blocks_list): points = [] for block in blocks: start, end = block block_index = index[start:end] block_value = varray[k, start:end] index_mask = self.index_mapper.range.mask_data(block_index) runs = [r for r in arg_find_runs(index_mask, "flat") \ if index_mask[r[0]] != 0] # Check to see if our data view region is between two points in the # index data. If so, then we have to reverse map our current view # into the appropriate index and draw the bracketing points. if runs == []: data_pt = self.map_data((self.x_mapper.low_pos, self.y_mapper.low_pos)) if self.index.sort_order == "none": indices = argsort(index) sorted_index = take(index, indices) sorted_value = take(varray[k], indices) sort = 1 else: sorted_index = index sorted_value = varray[k] if self.index.sort_order == "ascending": sort = 1 else: sort = -1 ndx = bin_search(sorted_index, data_pt, sort) if ndx == -1: # bin_search can return -1 if data_pt is outside the bounds # of the source data continue z = transpose(array((sorted_index[ndx:ndx+2], sorted_value[ndx:ndx+2]))) points.append(z) else: # Expand the width of every group of points so we draw the lines # up to their next point, outside the plot area data_end = len(index_mask) for run in runs: start, end = run if start != 0: start -= 1 if end != data_end: end += 1 run_data = transpose(array((block_index[start:end], block_value[start:end]))) points.append(run_data) line_points.append(points) self._cached_data_pts = line_points self._cache_valid = True return # See base_xy_plot.py for: ## def _downsample(self): ## def _downsample_vectorized(self): def _render(self, gc, line_points, selected_points=None): if len(line_points) == 0: return with gc: gc.set_antialias(True) gc.clip_to_rect(self.x, self.y, self.width, self.height) render = self._render_normal if selected_points is not None: gc.set_stroke_color(self.selected_color_) gc.set_line_width(self.line_width+10.0) gc.set_line_dash(self.selected_line_style_) render(gc, selected_points) if self.color_func is not None: # Existence of self.color_func overrides self.color. color_func = self.color_func else: color_func = lambda k: self.color_ tmp = list(enumerate(line_points)) # Note: the list is reversed for testing with _render_filled. for k, points in reversed(tmp): color = color_func(k) # Apply the alpha alpha = color[-1] if len(color) == 4 else 1 color = color[:3] + (alpha * self.alpha,) gc.set_stroke_color(color) gc.set_line_width(self.line_width) gc.set_line_dash(self.line_style_) render(gc, points) # Draw the default axes, if necessary self._draw_default_axes(gc) def _render_normal(self, gc, points): for ary in points: if len(ary) > 0: gc.begin_path() gc.lines(ary) gc.stroke_path() return def _render_icon(self, gc, x, y, width, height): with gc: gc.set_stroke_color(self.color_) gc.set_line_width(self.line_width) gc.set_line_dash(self.line_style_) gc.set_antialias(0) gc.move_to(x, y+height/2) gc.line_to(x+width, y+height/2) gc.stroke_path() def _alpha_changed(self): self.invalidate_draw() self.request_redraw() return def _color_changed(self): self.invalidate_draw() self.request_redraw() return def _line_style_changed(self): self.invalidate_draw() self.request_redraw() return def _line_width_changed(self): self.invalidate_draw() self.request_redraw() return def _amplitude_changed(self): self.value.data_changed = True self.invalidate_draw() self.request_redraw() return def __getstate__(self): state = super(MultiLinePlot,self).__getstate__() for key in ['traits_view']: if state.has_key(key): del state[key] return state chaco-4.5.0/chaco/overlays/0000755000076600000240000000000012426466422016306 5ustar jrocherstaff00000000000000chaco-4.5.0/chaco/overlays/__init__.py0000644000076600000240000000000012426452312020376 0ustar jrocherstaff00000000000000chaco-4.5.0/chaco/overlays/aligned_container_overlay.py0000644000076600000240000000505012426452312024057 0ustar jrocherstaff00000000000000"""An overlay that aligns itself to the plot """ from traits.api import Enum, Any from container_overlay import ContainerOverlay class AlignedContainerOverlay(ContainerOverlay): """ Container overlay that aligns itself to the plot This overlay takes an alignment which specifies which corner of its container it should align itself with ("ur", "ul", "ll", or "lr"). For tooltip or customizable behaviour, an alternative_position trait can be specified which gives the point to draw the component at, and the align is use to lay the container out relative to that position. """ # XXX allow 'c' for center as an alignment option? # XXX make this alignment stuff a Container subclass? A generic mixin? # Alignment of the text in the box: # # * "ur": upper right # * "ul": upper left # * "ll": lower left # * "lr": lower right align = Enum("ur", "ul", "ll", "lr") # This allows subclasses to specify an alternate position for the root # of the text box. Must be a sequence of length 2. alternate_position = Any def overlay(self, other, gc, view_bounds, mode): self._compute_position(other) self.draw(gc, view_bounds, mode) # XXX should this method really be _do_layout? def _compute_position(self, component): """ Given the alignment and size of the overlay, position it. """ if self.layout_needed: self.do_layout() valign, halign = self.align if self.alternate_position: x, y = self.alternate_position if valign == "u": self.outer_y = component.y + y else: self.outer_y2 = component.y + y if halign == "r": self.outer_x = component.x + x else: self.outer_x2 = component.x + x else: if valign == "u": self.outer_y2 = component.y2 else: self.outer_y = component.y if halign == "r": self.outer_x2 = component.x2 else: self.outer_x = component.x # attempt to get the box entirely within the component # (prefer expanding to top-right if we cover entire component) if self.x2 > component.x2: self.x2 = component.x2 if self.y2 > component.y2: self.y2 = component.y2 if self.x < component.x: self.x = component.x if self.y < component.y: self.y = component.y chaco-4.5.0/chaco/overlays/api.py0000644000076600000240000000047612426452312017431 0ustar jrocherstaff00000000000000from databox import DataBox from container_overlay import ContainerOverlay from aligned_container_overlay import AlignedContainerOverlay from text_grid_overlay import TextGridOverlay from simple_inspector_overlay import SimpleInspectorOverlay, basic_formatter, \ datetime_formatter, date_formatter, time_formatter chaco-4.5.0/chaco/overlays/container_overlay.py0000644000076600000240000000244612426452312022402 0ustar jrocherstaff00000000000000"""Plot overlay which is an Enable Container This module provides an Enable Container subclass which renders itself into the overlay of a plot. This allows the easy use of standard Enable components in plot overlays. """ from traits.api import Instance from enable.api import Container, Component from chaco.api import PlotComponent class ContainerOverlay(Container, PlotComponent): """ Container which is also a Chaco plot overlay Since this is an Enable container, any Components that it contains will be rendered into the overlay layer of the plot. """ # XXX this works, but I'm not sure that it's quite right. # The component that this object overlays. This can be None. By default, if # this object is called to draw(), it tries to render onto this component. component = Instance(Component) draw_layer = "overlay" # The background color (overrides PlotComponent). # Typically, an overlay does not render a background. bgcolor = "transparent" unified_draw = True auto_size = True def overlay(self, other, gc, view_bounds, mode): self.draw(gc, view_bounds, mode) def _request_redraw(self): if self.component is not None: self.component.request_redraw() super(ContainerOverlay, self)._request_redraw() chaco-4.5.0/chaco/overlays/coordinate_line_overlay.py0000644000076600000240000000612612426452312023555 0ustar jrocherstaff00000000000000""" An overlay for drawing "infinite" vertical and horizontal lines. This module defines the CoordinateLineOverlay class, a Chaco overlay for Plot (and similar) objects. """ from __future__ import with_statement from traits.api import Instance, Float, Array from enable.api import black_color_trait, LineStyle, Component from chaco.api import AbstractOverlay class CoordinateLineOverlay(AbstractOverlay): # The data coordinates of the lines to be drawn perpendicular to the # index axis. index_data = Array # The data coordinates of the lines to be drawn perpendicular to the # value axis. value_data = Array # Width of the lines. line_width = Float(1.0) # Color of the lines. color = black_color_trait # Style of the lines ('solid', 'dash' or 'dot'). line_style = LineStyle # The component that this tool overlays. This must be a Component with # the following attributes: # x, y, x2, y2 # The screen coordinates of the corners of the component. # orientation ('h' or 'v') # The orientation of the component, either horizontal or vertical. # This is the orientation of the index axis. # index_mapper # index_mapper.map_screen maps `index_data` to screen coordinates. # value_mapper # value_mapper.map_screen maps `value_data` to screen coordinates. # Typically this will be a Plot instance. component = Instance(Component) #---------------------------------------------------------------------- # Override AbstractOverlay methods #---------------------------------------------------------------------- def overlay(self, component, gc, view_bounds, mode="normal"): comp = self.component x_pts = comp.index_mapper.map_screen(self.index_data) y_pts = comp.value_mapper.map_screen(self.value_data) if comp.orientation == "v": x_pts, y_pts = y_pts, x_pts with gc: # Set the line color and style parameters. gc.set_stroke_color(self.color_) gc.set_line_width(self.line_width) gc.set_line_dash(self.line_style_) # Draw the vertical lines. for screen_x in x_pts: self._draw_vertical_line(gc, screen_x) # Draw the horizontal lines. for screen_y in y_pts: self._draw_horizontal_line(gc, screen_y) #---------------------------------------------------------------------- # Private methods #---------------------------------------------------------------------- def _draw_vertical_line(self, gc, screen_x): if screen_x < self.component.x or screen_x > self.component.x2: return gc.move_to(screen_x, self.component.y) gc.line_to(screen_x, self.component.y2) gc.stroke_path() def _draw_horizontal_line(self, gc, screen_y): if screen_y < self.component.y or screen_y > self.component.y2: return gc.move_to(self.component.x, screen_y,) gc.line_to(self.component.x2, screen_y) gc.stroke_path() chaco-4.5.0/chaco/overlays/databox.py0000644000076600000240000001517412426452312020303 0ustar jrocherstaff00000000000000 from __future__ import with_statement from traits.api import (Bool, Enum, Float, Int, CList, Property, Trait, on_trait_change) from enable.api import ColorTrait from chaco.api import AbstractOverlay class DataBox(AbstractOverlay): """ An overlay that is a box defined by data space coordinates. This can be used as a base class for various kinds of zoom boxes. Unlike the "momentary" zoom box drawn for the ZoomTool, a ZoomBox is a more permanent visual component. """ data_position = Property data_bounds = Property # Should the zoom box stay attached to the image or to the screen if the # component moves underneath it? # TODO: This basically works, but the problem is that it responds to both # changes in X and Y independently. The DataRange2D needs to be updated # to reflect changes from its two DataRange1Ds. The PanTool and ZoomTool # need to be improved that they change both dimensions at once. affinity = Enum("image", "screen") #------------------------------------------------------------------------- # Appearance properties (for Box mode) #------------------------------------------------------------------------- # The color of the selection box. color = ColorTrait("lightskyblue") # The alpha value to apply to **color** when filling in the selection # region. Because it is almost certainly useless to have an opaque zoom # rectangle, but it's also extremely useful to be able to use the normal # named colors from Enable, this attribute allows the specification of a # separate alpha value that replaces the alpha value of **color** at draw # time. alpha = Trait(0.3, None, Float) # The color of the outside selection rectangle. border_color = ColorTrait("dodgerblue") # The thickness of selection rectangle border. border_size = Int(1) #------------------------------------------------------------------------- # Private Traits #------------------------------------------------------------------------- _data_position = CList([0,0]) _data_bounds = CList([0,0]) _position_valid = False _bounds_valid = False # Are we in the middle of an event handler or a property setter _updating = Bool(False) def __init__(self, *args, **kw): super(DataBox, self).__init__(*args, **kw) if hasattr(self.component, "range2d"): self.component.range2d._xrange.on_trait_change(self.my_component_moved, "updated") self.component.range2d._yrange.on_trait_change(self.my_component_moved, "updated") elif hasattr(self.component, "x_mapper") and hasattr(self.component, "y_mapper"): self.component.x_mapper.range.on_trait_change(self.my_component_moved, "updated") self.component.y_mapper.range.on_trait_change(self.my_component_moved, "updated") else: raise RuntimeError("DataBox cannot find a suitable mapper on its component.") self.component.on_trait_change(self.my_component_resized, "bounds") self.component.on_trait_change(self.my_component_resized, "bounds_items") def overlay(self, component, gc, view_bounds=None, mode="normal"): if not self._position_valid: tmp = self.component.map_screen([self._data_position]) if len(tmp.shape) == 2: tmp = tmp[0] self._updating = True self.position = tmp self._updating = False self._position_valid = True if not self._bounds_valid: data_x2 = self._data_position[0] + self._data_bounds[0] data_y2 = self._data_position[1] + self._data_bounds[1] tmp = self.component.map_screen((data_x2, data_y2)) if len(tmp.shape) == 2: tmp = tmp[0] x2, y2 = tmp x, y = self.position self._updating = True self.bounds = [x2 - x, y2 - y] self._updating = False self._bounds_valid = True with gc: gc.set_antialias(0) gc.set_line_width(self.border_size) gc.set_stroke_color(self.border_color_) gc.clip_to_rect(component.x, component.y, component.width, component.height) rect = self.position + self.bounds if self.color != "transparent": if self.alpha: color = list(self.color_) if len(color) == 4: color[3] = self.alpha else: color += [self.alpha] else: color = self.color_ gc.set_fill_color(color) gc.rect(*rect) gc.draw_path() else: gc.rect(*rect) gc.stroke_path() return #------------------------------------------------------------------------- # Property setters/getters, event handlers #------------------------------------------------------------------------- def _get_data_position(self): return self._data_position def _set_data_position(self, val): self._data_position = val self._position_valid = False self.trait_property_changed("data_position", self._data_position) def _get_data_bounds(self): return self._data_bounds def _set_data_bounds(self, val): self._data_bounds = val self._bounds_valid = False self.trait_property_changed("data_bounds", self._data_bounds) @on_trait_change('position,position_items') def _update_position(self): if self._updating: return tmp = self.component.map_data(self.position) if len(tmp.shape) == 2: tmp = tmp.T[0] self._data_position = tmp self.trait_property_changed("data_position", self._data_position) @on_trait_change('bounds,bounds_items') def _update_bounds(self): if self._updating: return data_x2, data_y2 = self.component.map_data((self.x2, self.y2)) data_pos = self._data_position self._data_bounds = [data_x2 - data_pos[0], data_y2 - data_pos[1]] self.trait_property_changed("data_bounds", self._data_bounds) def my_component_moved(self): if self.affinity == "screen": # If we have screen affinity, then we need to take our current position # and map that back down into data coords self._update_position() self._update_bounds() self._bounds_valid = False self._position_valid = False def my_component_resized(self): self._bounds_valid = False self._position_valid = False chaco-4.5.0/chaco/overlays/simple_inspector_overlay.py0000644000076600000240000001363312426452312023777 0ustar jrocherstaff00000000000000"""A simple inspector overlay for plots This module provides the SimpleInspectorOverlay for displaying information gathered from an inspector tool in a TextGrid. By default it is configured to work with a SimpleInspectorTool. The module also provides some helper factory functions for creating text formatters for dictionary values. """ from numpy import array from traits.api import Any, List, Callable, Enum, Bool from text_grid_overlay import TextGridOverlay def basic_formatter(key, decimals): """Create a basic ': ' formatting function This factory creates a function that formats a specified key and with a numerical value from a dictionary into a string. Parameters ---------- key The dictionary key to format. decimals The number of decimal places to show. Returns ------- format A factory function that takes a dictionary and returns a string. """ format_string = '%s: %%(%s).%df' % (key, key, decimals) def format(**kwargs): return format_string % kwargs return format def datetime_formatter(key, time_format='%Y/%m/%d %H:%M:%S'): """Create a datetime formatting function This factory creates a function that formats a specified key and with a timestamp value from a dictionary into a string. Parameters ---------- key The dictionary key to format. The corresponding value should be a timestamp. time_format A format string suitable for strftime(). Returns ------- format A factory function that takes a dictionary and returns a string. """ import datetime def format(**kwargs): dt = datetime.datetime.fromtimestamp(kwargs[key]) return key+': '+dt.strftime(time_format) return format def time_formatter(key): """Create a time formatting function This factory creates a function that formats a specified key and with a timestamp value from a dictionary into a 'HH:MM:SS' format string. Parameters ---------- key The dictionary key to format. The corresponding value should be a timestamp. Returns ------- format A factory function that takes a dictionary and returns a string. """ return datetime_formatter(key, time_format='%H:%M:%S') def date_formatter(key): """Create a date formatting function This factory creates a function that formats a specified key and with a timestamp value from a dictionary into a 'yyyy/mm/dd' format string. Parameters ---------- key The dictionary key to format. The corresponding value should be a timestamp. Returns ------- format A factory function that takes a dictionary and returns a string. """ return datetime_formatter(key, time_format='%Y/%m/%d') class SimpleInspectorOverlay(TextGridOverlay): """ Simple inspector overlay for plots This is a simple overlay that listens for new_value events on a SimpleInspectorTool and displays formatted values in a grid. By default this displays the 'x' and 'y' values provided by the SimpleInspectorTool, but instances can provide a field_formatters trait which is a list of lists of callables which extract values from a dictionary and formats them. Each callable corresponds to a cell in the underlying TextGrid component. Although by default this works with the SimpleInspectorTool, with appropriate field_formatters this class can be used with any inspector tool that follows the same API. """ # XXX We should probably refactor this into a BaseInspectorOverlay # which handles the visibility and basic event handling, and smaller # version of this class which handles inserting values into a text grid # the inspector that I am listening to. This should have a new_value # event and a visible trait for me to listen to. inspector = Any # fields to display field_formatters = List(List(Callable)) # Anchor the text to the mouse? (If False, then the text is in one of the # corners.) Use the **align** trait to determine which corner. tooltip_mode = Bool(False) # The default state of the overlay is visible. visible = True # Whether the overlay should auto-hide and auto-show based on the # tool's location, or whether it should be forced to be hidden or visible. visibility = Enum("auto", True, False) ######################################################################### # Traits Handlers ######################################################################### def _field_formatters_default(self): return [[basic_formatter('x', 2)], [basic_formatter('y', 2)]] def _new_value_updated(self, event): if event is None: self.text_grid = array() if self.visibility == "auto": self.visibility = False elif self.visibility == "auto": self.visible = True if self.tooltip_mode: self.alternate_position = self.inspector.last_mouse_position d = event text = [] self.text_grid.string_array = array([[formatter(**d) for formatter in row] for row in self.field_formatters]) self.text_grid.request_redraw() def _visible_changed(self): if self.component: self.request_redraw() def _inspector_changed(self, old, new): if old: old.on_trait_event(self._new_value_updated, 'new_value', remove=True) old.on_trait_change(self._tool_visible_changed, "visible", remove=True) if new: new.on_trait_event(self._new_value_updated, 'new_value') new.on_trait_change(self._tool_visible_changed, "visible") self._tool_visible_changed() def _tool_visible_changed(self): self.visibility = self.inspector.visible if self.visibility != "auto": self.visible = self.visibility chaco-4.5.0/chaco/overlays/text_grid_overlay.py0000644000076600000240000000160712426452312022407 0ustar jrocherstaff00000000000000""" An overlay containing a TextGrid """ from traits.api import Instance from enable.text_grid import TextGrid from aligned_container_overlay import AlignedContainerOverlay class TextGridOverlay(AlignedContainerOverlay): """ Overlay for plots containing a TextGrid This subclass of AlignedContainerOverlay has a TextGrid which it displays. Subclasses or users are responsible for the content and formatting of the TextGrid. """ # The text grid component we contain. text_grid = Instance(TextGrid) # XXX put some delegated traits for the text_grid here? def _text_grid_changed(self, old, new): if old is not None: self.remove(old) if new is not None: self.add(new) def _text_grid_default(self): text_grid = TextGrid(font='modern 12', cell_border_width=0) self.add(text_grid) return text_grid chaco-4.5.0/chaco/pdf_graphics_context.py0000644000076600000240000002032212426452312021201 0ustar jrocherstaff00000000000000 # Major library imports import warnings try: # PDF imports from reportlab from reportlab.pdfgen.canvas import Canvas from reportlab.lib.pagesizes import letter, A4, landscape from reportlab.lib.units import inch, cm, mm, pica except ImportError: Canvas = None PdfPlotGraphicsContext = None from kiva.pdf import GraphicsContext PAGE_DPI = 72.0 PAGE_SIZE_MAP = { "letter": letter, "A4": A4, "landscape_letter": landscape(letter), "landscape_A4": landscape(A4) } UNITS_MAP = { "inch": inch, "cm": cm, "mm": mm, "pica": pica, } if Canvas is not None: class PdfPlotGraphicsContext(GraphicsContext): """ A convenience class for rendering PlotComponents onto PDF """ # The name of the file that this graphics context will use when # gc.save() is called without a filename being supplied. filename = "saved_plot.pdf" # The page size of the generated PDF pagesize = "letter" # Enum("letter", "A4") # A tuple (x, y, width, height) specifying the box into which the plot # should be rendered. **x** and **y** correspond to the lower-left # hand coordinates of the box in the coordinates of the page # (i.e. 0,0 is at the lower left). **width** and **height** can be # positive or negative; # if they are positive, they are interpreted as distances from (x,y); # if they are negative, they are interpreted as distances from the # right and top of the page, respectively. dest_box = (0.5, 0.5, -0.5, -0.5) # The units of the values in dest_box dest_box_units = "inch" # Enum("inch", "cm", "mm", "pica") def __init__(self, pdf_canvas=None, filename=None, pagesize=None, dest_box=None, dest_box_units=None): if filename: self.filename = filename if pagesize: self.pagesize = pagesize if dest_box: self.dest_box = dest_box if dest_box_units: self.dest_box_units = dest_box_units if pdf_canvas is None: pdf_canvas = self._create_new_canvas() GraphicsContext.__init__(self, pdf_canvas) def add_page(self): """ Adds a new page to the PDF canvas and makes that the current drawing target. """ if self.gc is None: warnings.warn("PDF Canvas has not been created yet.") return # Add the new page self.gc.showPage() # We'll need to call _initialize_page() before drawing self._page_initialized = False def render_component(self, component, container_coords=False, halign="center", valign="top"): """ Erases the current contents of the graphics context and renders the given component at the maximum possible scaling while preserving aspect ratio. Parameters ---------- component : Component The component to be rendered. container_coords : Boolean Whether to use coordinates of the component's container halign : "center", "left", "right" Determines the position of the component if it is narrower than the graphics context area (after scaling) valign : "center", "top", "bottom" Determiens the position of the component if it is shorter than the graphics context area (after scaling) Description ----------- If *container_coords* is False, then the (0,0) coordinate of this graphics context corresponds to the lower-left corner of the component's **outer_bounds**. If *container_coords* is True, then the method draws the component as it appears inside its container, i.e., it treats (0, 0) of the graphics context as the lower-left corner of the container's outer bounds. """ if not self._page_initialized: # Make sure the origin is set up as before. self._initialize_page(self.gc) x, y = component.outer_position if container_coords: width, height = component.container.bounds else: x = -x y = -y width, height = component.outer_bounds # Compute the correct scaling to fit the component into the # available canvas space while preserving aspect ratio. units = UNITS_MAP[self.dest_box_units] pagesize = PAGE_SIZE_MAP[self.pagesize] full_page_width = pagesize[0] full_page_height = pagesize[1] page_offset_x = self.dest_box[0] * units page_offset_y = self.dest_box[1] * units page_width = self.dest_box[2] * units page_height = self.dest_box[3] * units if page_width < 0: page_width += full_page_width - page_offset_x if page_height < 0: page_height += full_page_height - page_offset_y aspect = float(width) / float(height) if aspect >= page_width / page_height: # We are limited in width, so use widths to compute the scale # factor between pixels to page units. (We want square pixels, # so we re-use this scale for the vertical dimension.) scale = float(page_width) / float(width) trans_width = page_width trans_height = height * scale trans_x = x * scale trans_y = y * scale if valign == "top": trans_y += page_height - trans_height elif valign == "center": trans_y += (page_height - trans_height) / 2.0 else: # We are limited in height scale = page_height / height trans_height = page_height trans_width = width * scale trans_x = x * scale trans_y = y * scale if halign == "right": trans_x += page_width - trans_width elif halign == "center": trans_x += (page_width - trans_width) / 2.0 self.translate_ctm(trans_x, trans_y) self.scale_ctm(scale, scale) self.clip_to_rect(-x, -y, width, height) old_bb_setting = component.use_backbuffer component.use_backbuffer = False component.draw(self, view_bounds=(0, 0, width, height)) component.use_backbuffer = old_bb_setting return def save(self, filename=None): self.gc.save() def _create_new_canvas(self): """ Create the PDF canvas context. """ x, y, w, h, = self._get_bounding_box() if w < 0 or h < 0: self.gc = None return pagesize = PAGE_SIZE_MAP[self.pagesize] gc = Canvas(filename=self.filename, pagesize=pagesize) self._initialize_page(gc) return gc def _get_bounding_box(self): """ Compute the bounding rect of a page. """ pagesize = PAGE_SIZE_MAP[self.pagesize] units = UNITS_MAP[self.dest_box_units] x = self.dest_box[0] * units y = self.dest_box[1] * units w = self.dest_box[2] * units h = self.dest_box[3] * units if w < 0: w += pagesize[0] * units * inch / PAGE_DPI if h < 0: h += pagesize[1] * units * inch / PAGE_DPI if w < 0 or h < 0: warnings.warn("Margins exceed page dimensions.") return x, y, w, h def _initialize_page(self, gc): """ Make sure the origin is set to something consistent. """ x, y, w, h, = self._get_bounding_box() gc.translate(x, y) path = gc.beginPath() path.rect(0, 0, w, h) gc.clipPath(path, stroke=0, fill=0) self._page_initialized = True chaco-4.5.0/chaco/plot.py0000644000076600000240000013653012426462425016001 0ustar jrocherstaff00000000000000""" Defines the Plot class. """ # Major library imports import itertools import warnings from numpy import arange, array, ndarray, linspace from types import FunctionType # Enthought library imports from traits.api import Delegate, Dict, Instance, Int, List, Property, Str # Local, relative imports from abstract_colormap import AbstractColormap from abstract_data_source import AbstractDataSource from abstract_plot_data import AbstractPlotData from array_data_source import ArrayDataSource from array_plot_data import ArrayPlotData from base_xy_plot import BaseXYPlot from barplot import BarPlot from candle_plot import CandlePlot from colormapped_scatterplot import ColormappedScatterPlot from contour_line_plot import ContourLinePlot from contour_poly_plot import ContourPolyPlot from cmap_image_plot import CMapImagePlot from data_range_1d import DataRange1D from data_view import DataView from default_colormaps import Spectral from grid_data_source import GridDataSource from grid_mapper import GridMapper from image_data import ImageData from image_plot import ImagePlot from legend import Legend from lineplot import LinePlot from linear_mapper import LinearMapper from log_mapper import LogMapper from plot_label import PlotLabel from polygon_plot import PolygonPlot from scatterplot import ScatterPlot from filled_line_plot import FilledLinePlot from quiverplot import QuiverPlot #----------------------------------------------------------------------------- # The Plot class #----------------------------------------------------------------------------- class Plot(DataView): """ Represents a correlated set of data, renderers, and axes in a single screen region. A Plot can reference an arbitrary amount of data and can have an unlimited number of renderers on it, but it has a single X-axis and a single Y-axis for all of its associated data. Therefore, there is a single range in X and Y, although there can be many different data series. A Plot also has a single set of grids and a single background layer for all of its renderers. It cannot be split horizontally or vertically; to do so, create a VPlotContainer or HPlotContainer and put the Plots inside those. Plots can be overlaid as well; be sure to set the **bgcolor** of the overlaying plots to "none" or "transparent". A Plot consists of composable sub-plots. Each of these is created or destroyed using the plot() or delplot() methods. Every time that new data is used to drive these sub-plots, it is added to the Plot's list of data and data sources. Data sources are reused whenever possible; in order to have the same actual array drive two de-coupled data sources, create those data sources before handing them to the Plot. """ #------------------------------------------------------------------------ # Data-related traits #------------------------------------------------------------------------ # The PlotData instance that drives this plot. data = Instance(AbstractPlotData) # Mapping of data names from self.data to their respective datasources. datasources = Dict(Str, Instance(AbstractDataSource)) #------------------------------------------------------------------------ # General plotting traits #------------------------------------------------------------------------ # Mapping of plot names to *lists* of plot renderers. plots = Dict(Str, List) # The default index to use when adding new subplots. default_index = Instance(AbstractDataSource) # Optional mapper for the color axis. Not instantiated until first use; # destroyed if no color plots are on the plot. color_mapper = Instance(AbstractColormap) # List of colors to cycle through when auto-coloring is requested. Picked # and ordered to be red-green color-blind friendly, though should not # be an issue for blue-yellow. auto_colors = List(["green", "lightgreen", "blue", "lightblue", "red", "pink", "darkgray", "silver"]) # index into auto_colors list _auto_color_idx = Int(-1) _auto_edge_color_idx = Int(-1) _auto_face_color_idx = Int(-1) # Mapping of renderer type string to renderer class # This can be overriden to customize what renderer type the Plot # will instantiate for its various plotting methods. renderer_map = Dict(dict(line = LinePlot, bar = BarPlot, scatter = ScatterPlot, polygon = PolygonPlot, filled_line = FilledLinePlot, cmap_scatter = ColormappedScatterPlot, img_plot = ImagePlot, cmap_img_plot = CMapImagePlot, contour_line_plot = ContourLinePlot, contour_poly_plot = ContourPolyPlot, candle = CandlePlot, quiver = QuiverPlot,)) #------------------------------------------------------------------------ # Annotations and decorations #------------------------------------------------------------------------ # The title of the plot. title = Property() # The font to use for the title. title_font = Property() # Convenience attribute for title.overlay_position; can be "top", # "bottom", "left", or "right". title_position = Property() # Use delegates to expose the other PlotLabel attributes of the plot title title_text = Delegate("_title", prefix="text", modify=True) title_color = Delegate("_title", prefix="color", modify=True) title_angle = Delegate("_title", prefix="angle", modify=True) # The PlotLabel object that contains the title. _title = Instance(PlotLabel) # The legend on the plot. legend = Instance(Legend) # Convenience attribute for legend.align; can be "ur", "ul", "ll", "lr". legend_alignment = Property #------------------------------------------------------------------------ # Public methods #------------------------------------------------------------------------ def __init__(self, data=None, **kwtraits): if 'origin' in kwtraits: self.default_origin = kwtraits.pop('origin') if "title" in kwtraits: title = kwtraits.pop("title") else: title = None super(Plot, self).__init__(**kwtraits) if data is not None: if isinstance(data, AbstractPlotData): self.data = data elif type(data) in (ndarray, tuple, list): self.data = ArrayPlotData(data) else: raise ValueError, "Don't know how to create PlotData for data" \ "of type " + str(type(data)) if not self._title: self._title = PlotLabel(font="swiss 16", visible=False, overlay_position="top", component=self) if title is not None: self.title = title if not self.legend: self.legend = Legend(visible=False, align="ur", error_icon="blank", padding=10, component=self) # ensure that we only get displayed once by new_window() self._plot_ui_info = None return def add_xy_plot(self, index_name, value_name, renderer_factory, name=None, origin=None, **kwds): """ Add a BaseXYPlot renderer subclass to this Plot. Parameters ---------- index_name : str The name of the index datasource. value_name : str The name of the value datasource. renderer_factory : callable The callable that creates the renderer. name : string (optional) The name of the plot. If None, then a default one is created (usually "plotNNN"). origin : string (optional) Which corner the origin of this plot should occupy: "bottom left", "top left", "bottom right", "top right" **kwds : Additional keywords to pass to the factory. """ if name is None: name = self._make_new_plot_name() if origin is None: origin = self.default_origin index = self._get_or_create_datasource(index_name) self.index_range.add(index) value = self._get_or_create_datasource(value_name) self.value_range.add(value) if self.index_scale == "linear": imap = LinearMapper(range=self.index_range) else: imap = LogMapper(range=self.index_range) if self.value_scale == "linear": vmap = LinearMapper(range=self.value_range) else: vmap = LogMapper(range=self.value_range) renderer = renderer_factory( index = index, value = value, index_mapper = imap, value_mapper = vmap, orientation = self.orientation, origin = origin, **kwds ) self.add(renderer) self.plots[name] = [renderer] self.invalidate_and_redraw() return self.plots[name] def plot(self, data, type="line", name=None, index_scale="linear", value_scale="linear", origin=None, **styles): """ Adds a new sub-plot using the given data and plot style. Parameters ---------- data : string, tuple(string), list(string) The data to be plotted. The type of plot and the number of arguments determines how the arguments are interpreted: one item: (line/scatter) The data is treated as the value and self.default_index is used as the index. If **default_index** does not exist, one is created from arange(len(*data*)) two or more items: (line/scatter) Interpreted as (index, value1, value2, ...). Each index,value pair forms a new plot of the type specified. two items: (cmap_scatter) Interpreted as (value, color_values). Uses **default_index**. three or more items: (cmap_scatter) Interpreted as (index, val1, color_val1, val2, color_val2, ...) type : comma-delimited string of "line", "scatter", "cmap_scatter" The types of plots to add. name : string The name of the plot. If None, then a default one is created (usually "plotNNN"). index_scale : string The type of scale to use for the index axis. If not "linear", then a log scale is used. value_scale : string The type of scale to use for the value axis. If not "linear", then a log scale is used. origin : string Which corner the origin of this plot should occupy: "bottom left", "top left", "bottom right", "top right" styles : series of keyword arguments attributes and values that apply to one or more of the plot types requested, e.g.,'line_color' or 'line_width'. Examples -------- :: plot("my_data", type="line", name="myplot", color=lightblue) plot(("x-data", "y-data"), type="scatter") plot(("x", "y1", "y2", "y3")) Returns ------- [renderers] -> list of renderers created in response to this call to plot() """ if len(data) == 0: return if isinstance(data, basestring): data = (data,) self.index_scale = index_scale self.value_scale = value_scale # TODO: support lists of plot types plot_type = type if name is None: name = self._make_new_plot_name() if origin is None: origin = self.default_origin if plot_type in ("line", "scatter", "polygon", "bar", "filled_line"): # Tie data to the index range if len(data) == 1: if self.default_index is None: # Create the default index based on the length of the first # data series value = self._get_or_create_datasource(data[0]) self.default_index = ArrayDataSource(arange(len(value.get_data())), sort_order="none") self.index_range.add(self.default_index) index = self.default_index else: index = self._get_or_create_datasource(data[0]) if self.default_index is None: self.default_index = index self.index_range.add(index) data = data[1:] # Tie data to the value_range and create the renderer for each data new_plots = [] simple_plot_types = ("line", "scatter") for value_name in data: value = self._get_or_create_datasource(value_name) self.value_range.add(value) if plot_type in simple_plot_types: cls = self.renderer_map[plot_type] # handle auto-coloring request if styles.get("color") == "auto": self._auto_color_idx = \ (self._auto_color_idx + 1) % len(self.auto_colors) styles["color"] = self.auto_colors[self._auto_color_idx] elif plot_type in ("polygon", "filled_line"): cls = self.renderer_map[plot_type] # handle auto-coloring request if styles.get("edge_color") == "auto": self._auto_edge_color_idx = \ (self._auto_edge_color_idx + 1) % len(self.auto_colors) styles["edge_color"] = self.auto_colors[self._auto_edge_color_idx] if styles.get("face_color") == "auto": self._auto_face_color_idx = \ (self._auto_face_color_idx + 1) % len(self.auto_colors) styles["face_color"] = self.auto_colors[self._auto_face_color_idx] elif plot_type == 'bar': cls = self.renderer_map[plot_type] # handle auto-coloring request if styles.get("color") == "auto": self._auto_color_idx = \ (self._auto_color_idx + 1) % len(self.auto_colors) styles["fill_color"] = self.auto_colors[self._auto_color_idx] else: raise ValueError("Unhandled plot type: " + plot_type) if self.index_scale == "linear": imap = LinearMapper(range=self.index_range, stretch_data=self.index_mapper.stretch_data) else: imap = LogMapper(range=self.index_range, stretch_data=self.index_mapper.stretch_data) if self.value_scale == "linear": vmap = LinearMapper(range=self.value_range, stretch_data=self.value_mapper.stretch_data) else: vmap = LogMapper(range=self.value_range, stretch_data=self.value_mapper.stretch_data) plot = cls(index=index, value=value, index_mapper=imap, value_mapper=vmap, orientation=self.orientation, origin = origin, **styles) self.add(plot) new_plots.append(plot) if plot_type == 'bar': # For bar plots, compute the ranges from the data to make the # plot look clean. def custom_index_func(data_low, data_high, margin, tight_bounds): """ Compute custom bounds of the plot along index (in data space). """ bar_width = styles.get('bar_width', cls().bar_width) plot_low = data_low - bar_width plot_high = data_high + bar_width return plot_low, plot_high if self.index_range.bounds_func is None: self.index_range.bounds_func = custom_index_func def custom_value_func(data_low, data_high, margin, tight_bounds): """ Compute custom bounds of the plot along value (in data space). """ plot_low = data_low - (data_high-data_low)*0.1 plot_high = data_high + (data_high-data_low)*0.1 return plot_low, plot_high if self.value_range.bounds_func is None: self.value_range.bounds_func = custom_value_func self.index_range.tight_bounds = False self.value_range.tight_bounds = False self.index_range.refresh() self.value_range.refresh() self.plots[name] = new_plots elif plot_type == "cmap_scatter": if len(data) != 3: raise ValueError("Colormapped scatter plots require (index, value, color) data") else: index = self._get_or_create_datasource(data[0]) if self.default_index is None: self.default_index = index self.index_range.add(index) value = self._get_or_create_datasource(data[1]) self.value_range.add(value) color = self._get_or_create_datasource(data[2]) if not styles.has_key("color_mapper"): raise ValueError("Scalar 2D data requires a color_mapper.") colormap = styles.pop("color_mapper", None) if self.color_mapper is not None and self.color_mapper.range is not None: color_range = self.color_mapper.range else: color_range = DataRange1D() if isinstance(colormap, AbstractColormap): self.color_mapper = colormap if colormap.range is None: color_range.add(color) colormap.range = color_range elif callable(colormap): color_range.add(color) self.color_mapper = colormap(color_range) else: raise ValueError("Unexpected colormap %r in plot()." % colormap) if self.index_scale == "linear": imap = LinearMapper(range=self.index_range, stretch_data=self.index_mapper.stretch_data) else: imap = LogMapper(range=self.index_range, stretch_data=self.index_mapper.stretch_data) if self.value_scale == "linear": vmap = LinearMapper(range=self.value_range, stretch_data=self.value_mapper.stretch_data) else: vmap = LogMapper(range=self.value_range, stretch_data=self.value_mapper.stretch_data) cls = self.renderer_map["cmap_scatter"] plot = cls(index=index, index_mapper=imap, value=value, value_mapper=vmap, color_data=color, color_mapper=self.color_mapper, orientation=self.orientation, origin=origin, **styles) self.add(plot) self.plots[name] = [plot] else: raise ValueError("Unknown plot type: " + plot_type) return self.plots[name] def img_plot(self, data, name=None, colormap=None, xbounds=None, ybounds=None, origin=None, hide_grids=True, **styles): """ Adds image plots to this Plot object. If *data* has shape (N, M, 3) or (N, M, 4), then it is treated as RGB or RGBA (respectively) and *colormap* is ignored. If *data* is an array of floating-point data, then a colormap can be provided via the *colormap* argument, or the default of 'Spectral' will be used. *Data* should be in row-major order, so that xbounds corresponds to *data*'s second axis, and ybounds corresponds to the first axis. Parameters ---------- data : string The name of the data array in self.plot_data name : string The name of the plot; if omitted, then a name is generated. xbounds, ybounds : string, tuple, or ndarray Bounds where this image resides. Bound may be: a) names of data in the plot data; b) tuples of (low, high) in data space, c) 1D arrays of values representing the pixel boundaries (must be 1 element larger than underlying data), or d) 2D arrays as obtained from a meshgrid operation origin : string Which corner the origin of this plot should occupy: "bottom left", "top left", "bottom right", "top right" hide_grids : bool, default True Whether or not to automatically hide the grid lines on the plot styles : series of keyword arguments Attributes and values that apply to one or more of the plot types requested, e.g.,'line_color' or 'line_width'. """ if name is None: name = self._make_new_plot_name() if origin is None: origin = self.default_origin value = self._get_or_create_datasource(data) array_data = value.get_data() if len(array_data.shape) == 3: if array_data.shape[2] not in (3,4): raise ValueError("Image plots require color depth of 3 or 4.") cls = self.renderer_map["img_plot"] kwargs = dict(**styles) else: if colormap is None: if self.color_mapper is None: colormap = Spectral(DataRange1D(value)) else: colormap = self.color_mapper elif isinstance(colormap, AbstractColormap): if colormap.range is None: colormap.range = DataRange1D(value) else: colormap = colormap(DataRange1D(value)) self.color_mapper = colormap cls = self.renderer_map["cmap_img_plot"] kwargs = dict(value_mapper=colormap, **styles) return self._create_2d_plot(cls, name, origin, xbounds, ybounds, value, hide_grids, **kwargs) def contour_plot(self, data, type="line", name=None, poly_cmap=None, xbounds=None, ybounds=None, origin=None, hide_grids=True, **styles): """ Adds contour plots to this Plot object. Parameters ---------- data : string The name of the data array in self.plot_data, which must be floating point data. type : comma-delimited string of "line", "poly" The type of contour plot to add. If the value is "poly" and no colormap is provided via the *poly_cmap* argument, then a default colormap of 'Spectral' is used. name : string The name of the plot; if omitted, then a name is generated. poly_cmap : string The name of the color-map function to call (in chaco.default_colormaps) or an AbstractColormap instance to use for contour poly plots (ignored for contour line plots) xbounds, ybounds : string, tuple, or ndarray Bounds where this image resides. Bound may be: a) names of data in the plot data; b) tuples of (low, high) in data space, c) 1D arrays of values representing the pixel boundaries (must be 1 element larger than underlying data), or d) 2D arrays as obtained from a meshgrid operation origin : string Which corner the origin of this plot should occupy: "bottom left", "top left", "bottom right", "top right" hide_grids : bool, default True Whether or not to automatically hide the grid lines on the plot styles : series of keyword arguments Attributes and values that apply to one or more of the plot types requested, e.g.,'line_color' or 'line_width'. """ if name is None: name = self._make_new_plot_name() if origin is None: origin = self.default_origin value = self._get_or_create_datasource(data) if value.value_depth != 1: raise ValueError("Contour plots require 2D scalar field") if type == "line": cls = self.renderer_map["contour_line_plot"] kwargs = dict(**styles) # if colors is given as a factory func, use it to make a # concrete colormapper. Better way to do this? if "colors" in kwargs: cmap = kwargs["colors"] if isinstance(cmap, FunctionType): kwargs["colors"] = cmap(DataRange1D(value)) elif getattr(cmap, 'range', 'dummy') is None: cmap.range = DataRange1D(value) elif type == "poly": if poly_cmap is None: poly_cmap = Spectral(DataRange1D(value)) elif isinstance(poly_cmap, FunctionType): poly_cmap = poly_cmap(DataRange1D(value)) elif getattr(poly_cmap, 'range', 'dummy') is None: poly_cmap.range = DataRange1D(value) cls = self.renderer_map["contour_poly_plot"] kwargs = dict(color_mapper=poly_cmap, **styles) else: raise ValueError("Unhandled contour plot type: " + type) return self._create_2d_plot(cls, name, origin, xbounds, ybounds, value, hide_grids, **kwargs) def _process_2d_bounds(self, bounds, array_data, axis): """Transform an arbitrary bounds definition into a linspace. Process all the ways the user could have defined the x- or y-bounds of a 2d plot and return a linspace between the lower and upper range of the bounds. Parameters ---------- bounds : any User bounds definition array_data : 2D array The 2D plot data axis : int The axis along which the bounds are to be set """ num_ticks = array_data.shape[axis] + 1 if bounds is None: return arange(num_ticks) if type(bounds) is tuple: # create a linspace with the bounds limits return linspace(bounds[0], bounds[1], num_ticks) if type(bounds) is ndarray and len(bounds.shape) == 1: # bounds is 1D, but of the wrong size if len(bounds) != num_ticks: msg = ("1D bounds of an image plot needs to have 1 more " "element than its corresponding data shape, because " "they represent the locations of pixel boundaries.") raise ValueError(msg) else: return linspace(bounds[0], bounds[-1], num_ticks) if type(bounds) is ndarray and len(bounds.shape) == 2: # bounds is 2D, assumed to be a meshgrid # This is triggered when doing something like # >>> xbounds, ybounds = meshgrid(...) # >>> z = f(xbounds, ybounds) if bounds.shape != array_data.shape: msg = ("2D bounds of an image plot needs to have the same " "shape as the underlying data, because " "they are assumed to be generated from meshgrids.") raise ValueError(msg) else: if axis == 0: bounds = bounds[:,0] else: bounds = bounds[0,:] interval = bounds[1] - bounds[0] return linspace(bounds[0], bounds[-1]+interval, num_ticks) raise ValueError("bounds must be None, a tuple, an array, " "or a PlotData name") def _create_2d_plot(self, cls, name, origin, xbounds, ybounds, value_ds, hide_grids, **kwargs): if name is None: name = self._make_new_plot_name() if origin is None: origin = self.default_origin array_data = value_ds.get_data() # process bounds to get linspaces if isinstance(xbounds, basestring): xbounds = self._get_or_create_datasource(xbounds).get_data() xs = self._process_2d_bounds(xbounds, array_data, 1) if isinstance(ybounds, basestring): ybounds = self._get_or_create_datasource(ybounds).get_data() ys = self._process_2d_bounds(ybounds, array_data, 0) # Create the index and add its datasources to the appropriate ranges index = GridDataSource(xs, ys, sort_order=('ascending', 'ascending')) self.range2d.add(index) mapper = GridMapper(range=self.range2d, stretch_data_x=self.x_mapper.stretch_data, stretch_data_y=self.y_mapper.stretch_data) plot = cls(index=index, value=value_ds, index_mapper=mapper, orientation=self.orientation, origin=origin, **kwargs) if hide_grids: self.x_grid.visible = False self.y_grid.visible = False self.add(plot) self.plots[name] = [plot] return self.plots[name] def candle_plot(self, data, name=None, value_scale="linear", origin=None, **styles): """ Adds a new sub-plot using the given data and plot style. Parameters ---------- data : list(string), tuple(string) The names of the data to be plotted in the ArrayDataSource. The number of arguments determines how they are interpreted: (index, bar_min, bar_max) filled or outline-only bar extending from **bar_min** to **bar_max** (index, bar_min, center, bar_max) above, plus a center line of a different color at **center** (index, min, bar_min, bar_max, max) bar extending from **bar_min** to **bar_max**, with thin bars at **min** and **max** connected to the bar by a long stem (index, min, bar_min, center, bar_max, max) like above, plus a center line of a different color and configurable thickness at **center** name : string The name of the plot. If None, then a default one is created. value_scale : string The type of scale to use for the value axis. If not "linear", then a log scale is used. Styles ------ These are all optional keyword arguments. bar_color : string, 3- or 4-tuple The fill color of the bar; defaults to "auto". bar_line_color : string, 3- or 4-tuple The color of the rectangular box forming the bar. stem_color : string, 3- or 4-tuple (default = bar_line_color) The color of the stems reaching from the bar to the min and max values. center_color : string, 3- or 4-tuple (default = bar_line_color) The color of the line drawn across the bar at the center values. line_width : int (default = 1) The thickness, in pixels, of the outline around the bar. stem_width : int (default = line_width) The thickness, in pixels, of the stem lines center_width : int (default = line_width) The width, in pixels, of the line drawn across the bar at the center values. end_cap : bool (default = True) Whether or not to draw bars at the min and max extents of the error bar. Returns ------- [renderers] -> list of renderers created in response to this call. """ if len(data) == 0: return self.value_scale = value_scale if name is None: name = self._make_new_plot_name() if origin is None: origin = self.default_origin # Create the datasources if len(data) == 3: index, bar_min, bar_max = map(self._get_or_create_datasource, data) self.value_range.add(bar_min, bar_max) center = None min = None max = None elif len(data) == 4: index, bar_min, center, bar_max = map(self._get_or_create_datasource, data) self.value_range.add(bar_min, center, bar_max) min = None max = None elif len(data) == 5: index, min, bar_min, bar_max, max = \ map(self._get_or_create_datasource, data) self.value_range.add(min, bar_min, bar_max, max) center = None elif len(data) == 6: index, min, bar_min, center, bar_max, max = \ map(self._get_or_create_datasource, data) self.value_range.add(min, bar_min, center, bar_max, max) self.index_range.add(index) if styles.get("bar_color") == "auto" or styles.get("color") == "auto": self._auto_color_idx = \ (self._auto_color_idx + 1) % len(self.auto_colors) styles["color"] = self.auto_colors[self._auto_color_idx] if self.index_scale == "linear": imap = LinearMapper(range=self.index_range, stretch_data=self.index_mapper.stretch_data) else: imap = LogMapper(range=self.index_range, stretch_data=self.index_mapper.stretch_data) if self.value_scale == "linear": vmap = LinearMapper(range=self.value_range, stretch_data=self.value_mapper.stretch_data) else: vmap = LogMapper(range=self.value_range, stretch_data=self.value_mapper.stretch_data) cls = self.renderer_map["candle"] plot = cls(index = index, min_values = min, bar_min = bar_min, center_values = center, bar_max = bar_max, max_values = max, index_mapper = imap, value_mapper = vmap, orientation = self.orientation, origin = self.origin, **styles) self.add(plot) self.plots[name] = [plot] return [plot] def quiverplot(self, data, name=None, origin=None, **styles): """ Adds a new sub-plot using the given data and plot style. Parameters ---------- data : list(string), tuple(string) The names of the data to be plotted in the ArrayDataSource. There is only one combination accepted by this function: (index, value, vectors) index and value together determine the start coordinates of each vector. The vectors are an Nx2 name : string The name of the plot. If None, then a default one is created. origin : string Which corner the origin of this plot should occupy: "bottom left", "top left", "bottom right", "top right" Styles ------ These are all optional keyword arguments. line_color : string (default = "black") The color of the arrows line_width : float (default = 1.0) The thickness, in pixels, of the arrows. arrow_size : int (default = 5) The length, in pixels, of the arrowhead Returns ------- [renderers] -> list of renderers created in response to this call. """ if name is None: name = self._make_new_plot_name() if origin is None: origin = self.default_origin index, value, vectors = map(self._get_or_create_datasource, data) self.index_range.add(index) self.value_range.add(value) imap = LinearMapper(range=self.index_range, stretch_data=self.index_mapper.stretch_data) vmap = LinearMapper(range=self.value_range, stretch_data=self.value_mapper.stretch_data) cls = self.renderer_map["quiver"] plot = cls(index = index, value = value, vectors = vectors, index_mapper = imap, value_mapper = vmap, name = name, origin = origin, **styles ) self.add(plot) self.plots[name] = [plot] return [plot] def delplot(self, *names): """ Removes the named sub-plots. """ # This process involves removing the plots, then checking the index range # and value range for leftover datasources, and removing those if necessary. # Remove all the renderers from us (container) and create a set of the # datasources that we might have to remove from the ranges deleted_sources = set() for renderer in itertools.chain(*[self.plots.pop(name) for name in names]): self.remove(renderer) deleted_sources.add(renderer.index) deleted_sources.add(renderer.value) # Cull the candidate list of sources to remove by checking the other plots sources_in_use = set() for p in itertools.chain(*self.plots.values()): sources_in_use.add(p.index) sources_in_use.add(p.value) unused_sources = deleted_sources - sources_in_use - set([None]) # Remove the unused sources from all ranges for source in unused_sources: if source.index_dimension == "scalar": # Try both index and range, it doesn't hurt self.index_range.remove(source) self.value_range.remove(source) elif source.index_dimension == "image": self.range2d.remove(source) else: warnings.warn("Couldn't remove datasource from datarange.") return def hideplot(self, *names): """ Convenience function to sets the named plots to be invisible. Their renderers are not removed, and they are still in the list of plots. """ for renderer in itertools.chain(*[self.plots[name] for name in names]): renderer.visible = False return def showplot(self, *names): """ Convenience function to sets the named plots to be visible. """ for renderer in itertools.chain(*[self.plots[name] for name in names]): renderer.visible = True return def new_window(self, configure=False): """Convenience function that creates a window containing the Plot Don't call this if the plot is already displayed in a window. """ from chaco.ui.plot_window import PlotWindow if self._plot_ui_info is None: if configure: self._plot_ui_info = PlotWindow(plot=self).configure_traits() else: self._plot_ui_info = PlotWindow(plot=self).edit_traits() return self._plot_ui_info #------------------------------------------------------------------------ # Private methods #------------------------------------------------------------------------ def _make_new_plot_name(self): """ Returns a string that is not already used as a plot title. """ n = len(self.plots) plot_template = "plot%d" while 1: name = plot_template % n if name not in self.plots: break else: n += 1 return name def _get_or_create_datasource(self, name): """ Returns the data source associated with the given name, or creates it if it doesn't exist. """ if name not in self.datasources: data = self.data.get_data(name) if type(data) in (list, tuple): data = array(data) if isinstance(data, ndarray): if len(data.shape) == 1: ds = ArrayDataSource(data, sort_order="none") elif len(data.shape) == 2: ds = ImageData(data=data, value_depth=1) elif len(data.shape) == 3 and data.shape[2] in (3,4): ds = ImageData(data=data, value_depth=int(data.shape[2])) else: raise ValueError("Unhandled array shape in creating new " "plot: %s" % str(data.shape)) elif isinstance(data, AbstractDataSource): ds = data else: raise ValueError("Couldn't create datasource for data of " "type %s" % type(data)) self.datasources[name] = ds return self.datasources[name] #------------------------------------------------------------------------ # Event handlers #------------------------------------------------------------------------ def _color_mapper_changed(self): for plist in self.plots.values(): for plot in plist: plot.color_mapper = self.color_mapper self.invalidate_draw() def _data_changed(self, old, new): if old: old.on_trait_change(self._data_update_handler, "data_changed", remove=True) if new: new.on_trait_change(self._data_update_handler, "data_changed") def _data_update_handler(self, name, event): # event should be a dict with keys "added", "removed", and "changed", # per the comments in AbstractPlotData. if "removed" in event: for name in event["removed"]: del self.datasources[name] if "added" in event: for name in event["added"]: self._get_or_create_datasource(name) if "changed" in event: for name in event["changed"]: if name in self.datasources: source = self.datasources[name] source.set_data(self.data.get_data(name)) def _plots_items_changed(self, event): if self.legend: self.legend.plots = self.plots def _index_scale_changed(self, old, new): if old is None: return if new == old: return if not self.range2d: return if self.index_scale == "linear": imap = LinearMapper(range=self.index_range, screen_bounds=self.index_mapper.screen_bounds, stretch_data=self.index_mapper.stretch_data) else: imap = LogMapper(range=self.index_range, screen_bounds=self.index_mapper.screen_bounds, stretch_data=self.index_mapper.stretch_data) self.index_mapper = imap for key in self.plots: for plot in self.plots[key]: if not isinstance(plot, BaseXYPlot): raise ValueError("log scale only supported on XY plots") if self.index_scale == "linear": imap = LinearMapper(range=plot.index_range, screen_bounds=plot.index_mapper.screen_bounds, stretch_data=self.index_mapper.stretch_data) else: imap = LogMapper(range=plot.index_range, screen_bounds=plot.index_mapper.screen_bounds, stretch_data=self.index_mapper.stretch_data) plot.index_mapper = imap def _value_scale_changed(self, old, new): if old is None: return if new == old: return if not self.range2d: return if self.value_scale == "linear": vmap = LinearMapper(range=self.value_range, screen_bounds=self.value_mapper.screen_bounds, stretch_data=self.value_mapper.stretch_data) else: vmap = LogMapper(range=self.value_range, screen_bounds=self.value_mapper.screen_bounds, stretch_data=self.value_mapper.stretch_data) self.value_mapper = vmap for key in self.plots: for plot in self.plots[key]: if not isinstance(plot, BaseXYPlot): raise ValueError("log scale only supported on XY plots") if self.value_scale == "linear": vmap = LinearMapper(range=plot.value_range, screen_bounds=plot.value_mapper.screen_bounds, stretch_data=self.value_mapper.stretch_data) else: vmap = LogMapper(range=plot.value_range, screen_bounds=plot.value_mapper.screen_bounds, stretch_data=self.value_mapper.stretch_data) plot.value_mapper = vmap def __title_changed(self, old, new): self._overlay_change_helper(old, new) def _legend_changed(self, old, new): self._overlay_change_helper(old, new) if new: new.plots = self.plots def _handle_range_changed(self, name, old, new): """ Overrides the DataView default behavior. Primarily changes how the list of renderers is looked up. """ mapper = getattr(self, name+"_mapper") if mapper.range == old: mapper.range = new if old is not None: for datasource in old.sources[:]: old.remove(datasource) if new is not None: new.add(datasource) range_name = name + "_range" for renderer in itertools.chain(*self.plots.values()): if hasattr(renderer, range_name): setattr(renderer, range_name, new) #------------------------------------------------------------------------ # Property getters and setters #------------------------------------------------------------------------ def _set_legend_alignment(self, align): if self.legend: self.legend.align = align def _get_legend_alignment(self): if self.legend: return self.legend.align else: return None def _set_title(self, text): self._title.text = text if text.strip() != "": self._title.visible = True else: self._title.visible = False def _get_title(self): return self._title.text def _set_title_position(self, pos): if self._title is not None: self._title.overlay_position = pos def _get_title_position(self): if self._title is not None: return self._title.overlay_position else: return None def _set_title_font(self, font): old_font = self._title.font self._title.font = font self.trait_property_changed("title_font", old_font, font) def _get_title_font(self): return self._title.font chaco-4.5.0/chaco/plot_canvas.py0000644000076600000240000000603312426452312017320 0ustar jrocherstaff00000000000000 # Enthought library imports from enable.api import Canvas from traits.api import Instance, Tuple # Local, relative chaco imports from plot_containers import DEFAULT_DRAWING_ORDER class PlotCanvas(Canvas): """ The PlotCanvas is basically like Canvas, but we inherit some behaviors from PlotComponent as well. Some methods are redefined in here to explicitly make sure we get the right dispatch order. """ #------------------------------------------------------------------------- # Public traits #------------------------------------------------------------------------- # Default size to use for resizable components placed onto us. default_component_size = Tuple(200, 200) #------------------------------------------------------------------------- # Inherited traits #------------------------------------------------------------------------- # Explicitly use the Chaco drawing order instead of the Enable one draw_order = Instance(list, args=(DEFAULT_DRAWING_ORDER,)) # Redefine the enable-level set of layers to use "plot" instead # of "mainlayer". This is the same thing that BasePlotContainer does. container_under_layers = Tuple("background", "image", "underlay", "plot") # Override the definition from Component use_backbuffer = False #------------------------------------------------------------------------- # Inherited methods #------------------------------------------------------------------------- def draw(self, gc, view_bounds=None, mode="default"): if self.view_bounds is None: self.view_bounds = view_bounds if self.layout_needed: self.do_layout() self._draw(gc, view_bounds, mode) def _dispatch_draw(self, layer, gc, view_bounds, mode): Canvas._dispatch_draw(self, layer, gc, view_bounds, mode) def get_preferred_size(self, components=None): """ Returns the size (width,height) that is preferred for this components. """ if self.view_bounds is not None: x, y, x2, y2 = self.view_bounds else: x, y, x2, y2 = self._bounding_box return (x2 - x + 1, y2 - y + 1) def _do_layout(self): for component in self.components + self.underlays + self.overlays: if not self._should_layout(component): continue bounds = list(component.outer_bounds) pref_size = list(component.get_preferred_size()) if "h" in component.resizable: if pref_size[0] > 0: bounds[0] = pref_size[0] else: bounds[0] = self.default_component_size[0] if "v" in component.resizable: if pref_size[1] > 0: bounds[1] = pref_size[1] else: bounds[1] = self.default_component_size[1] component.outer_bounds = bounds for component in self.components + self.underlays + self.overlays: component.do_layout() return chaco-4.5.0/chaco/plot_canvas_toolbar.py0000644000076600000240000000437312426452312021047 0ustar jrocherstaff00000000000000 from traits.api import Any, Enum, Int from enable.drawing.api import ToolbarButton # Local, relative imports from plot_containers import VPlotContainer from plot_component import PlotComponent class PlotCanvasToolbar(VPlotContainer): # The placement of the toolbar over the canvas align = Enum("ur", "ul", "ll", "lr", "left", "right", "top", "bottom") # The spacing between buttons button_spacing = Int(5) # The (optional) component that we overlay component = Any # Override some inherited traits spacing = 5 fit_components = "hv" resizable = "hv" halign = "center" valign = "center" fill_padding = False unified_draw = True def overlay(self, component, gc, view_bounds=None, mode="normal"): """ Allows the toolbar to behave like an overlay """ self._do_layout() pref_size = self.get_preferred_size() # Special check for when we are overlaying an instance of enable.Canvas if hasattr(component, 'view_bounds'): cx, cy, cx2, cy2 = component.view_bounds cwidth = cx2 - cx + 1 cheight = cy2 - cy + 1 else: cx, cy = component.position cheight, cwidth = component.bounds if self.align in ("ur", "ul", "top"): y = cy + cheight - self.outer_height elif self.align in ("lr", "ll", "bottom"): y = cy else: y = cy + (cheight - pref_size[1])/2 if self.align in ("ur", "lr", "right"): x = cx + cwidth - self.outer_width elif self.align in ("ul", "ll", "left"): x = cx else: x = cx + (cwidth - pref_size[0])/2 self.outer_position = [x, y] VPlotContainer._draw(self, gc, view_bounds, mode) def _request_redraw(self): # Reproduce the behavior in AbstractOverlay to dispatch a # redraw event up to our overlaid component if self.component is not None: self.component.request_redraw() super(PlotCanvasToolbar, self)._request_redraw() return class PlotToolbarButton(PlotComponent, ToolbarButton): label_font = "Arial 12" unified_draw = True def _draw_plot(self, *args, **kw): return self._draw_mainlayer(*args, **kw) chaco-4.5.0/chaco/plot_component.py0000644000076600000240000000516012426452312020047 0ustar jrocherstaff00000000000000""" Defines the PlotComponent class. """ # Enthought library imports from enable.api import Component from enable.kiva_graphics_context import GraphicsContext from traits.api import Bool, Instance, Str DEFAULT_DRAWING_ORDER = ["background", "image", "underlay", "plot", "selection", "border", "annotation", "overlay"] class PlotComponent(Component): """ PlotComponent is the base class for all plot-related visual components. It defines the various methods related to layout and tool handling, which virtually every subclass uses or needs to be aware of. Several of these top-level layout and draw methods have implementations that must not be overridden; instead, subclasses implement various protected stub methods. """ #------------------------------------------------------------------------ # Rendering control traits #------------------------------------------------------------------------ # The order in which various rendering classes on this component are drawn. # Note that if this component is placed in a container, in most cases # the container's draw order is used, since the container calls # each of its contained components for each rendering pass. # Typically, the definitions of the layers are: # # 1. 'background': Background image, shading # 2. 'image': A special layer for plots that render as images. This is in # a separate layer since these plots must all render before non-image # plots. # 3. 'underlay': Axes and grids # 4. 'plot': The main plot area itself # 5. 'selection': Selected content are rendered above normal plot elements # to make them stand out # 6. 'border': Plot borders # 7. 'annotation': Lines and text that are conceptually part of the "plot" # but need to be rendered on top of everything else in the plot # 8. 'overlay': Legends, selection regions, and other tool-drawn visual # elements draw_order = Instance(list, args=(DEFAULT_DRAWING_ORDER,)) # The default draw layer for Chaco plot components is the "plot" layer draw_layer = Str("plot") # Draw layers in **draw_order**? If False, use _do_draw() (for backwards # compatibility). use_draw_order = Bool(True) def _use_draw_order_changed(self, old, new): """ Handler to catch the case when someone is trying to use the old-style drawing mechanism, which is now unsupported. """ if new == False: raise RuntimeError("The old-style drawing mechanism is no longer " \ "supported in Chaco.") chaco-4.5.0/chaco/plot_containers.py0000644000076600000240000007063512426452312020223 0ustar jrocherstaff00000000000000""" Defines various plot container classes, including stacked, grid, and overlay. """ # Major library imports from numpy import amax, any, arange, array, cumsum, hstack, sum, zeros, zeros_like # Enthought library imports from traits.api import Any, Array, Either, Enum, Float, Instance, \ List, Property, Trait, Tuple, Int from enable.simple_layout import simple_container_get_preferred_size, \ simple_container_do_layout # Local relative imports from base_plot_container import BasePlotContainer __all__ = ["OverlayPlotContainer", "HPlotContainer", "VPlotContainer", \ "GridPlotContainer"] DEFAULT_DRAWING_ORDER = ["background", "image", "underlay", "plot", "selection", "border", "annotation", "overlay"] class OverlayPlotContainer(BasePlotContainer): """ A plot container that stretches all its components to fit within its space. All of its components must therefore be resizable. """ draw_order = Instance(list, args=(DEFAULT_DRAWING_ORDER,)) # Do not use an off-screen backbuffer. use_backbuffer = False # Cache (width, height) of the container's preferred size. _cached_preferred_size = Tuple def get_preferred_size(self, components=None): """ Returns the size (width,height) that is preferred for this component. Overrides PlotComponent """ return simple_container_get_preferred_size(self, components=components) def _do_layout(self): """ Actually performs a layout (called by do_layout()). """ simple_container_do_layout(self) return class StackedPlotContainer(BasePlotContainer): """ Base class for 1-D stacked plot containers, both horizontal and vertical. """ draw_order = Instance(list, args=(DEFAULT_DRAWING_ORDER,)) # The dimension along which to stack components that are added to # this container. stack_dimension = Enum("h", "v") # The "other" dimension, i.e., the dual of the stack dimension. other_dimension = Enum("v", "h") # The index into obj.position and obj.bounds that corresponds to # **stack_dimension**. This is a class-level and not an instance-level # attribute. It must be 0 or 1. stack_index = 0 def get_preferred_size(self, components=None): """ Returns the size (width,height) that is preferred for this component. Overrides PlotComponent. """ if self.fixed_preferred_size is not None: self._cached_preferred_size = self.fixed_preferred_size return self.fixed_preferred_size if self.resizable == "": self._cached_preferred_size = self.outer_bounds[:] return self.outer_bounds if components is None: components = self.components ndx = self.stack_index other_ndx = 1 - ndx no_visible_components = True total_size = 0 max_other_size = 0 for component in components: if not self._should_layout(component): continue no_visible_components = False pref_size = component.get_preferred_size() total_size += pref_size[ndx] + self.spacing if pref_size[other_ndx] > max_other_size: max_other_size = pref_size[other_ndx] if total_size >= self.spacing: total_size -= self.spacing if (self.stack_dimension not in self.resizable) and \ (self.stack_dimension not in self.fit_components): total_size = self.bounds[ndx] elif no_visible_components or (total_size == 0): total_size = self.default_size[ndx] if (self.other_dimension not in self.resizable) and \ (self.other_dimension not in self.fit_components): max_other_size = self.bounds[other_ndx] elif no_visible_components or (max_other_size == 0): max_other_size = self.default_size[other_ndx] if ndx == 0: self._cached_preferred_size = (total_size + self.hpadding, max_other_size + self.vpadding) else: self._cached_preferred_size = (max_other_size + self.hpadding, total_size + self.vpadding) return self._cached_preferred_size def _do_stack_layout(self, components, align): """ Helper method that does the actual work of layout. """ size = list(self.bounds) if self.fit_components != "": self.get_preferred_size() if "h" in self.fit_components: size[0] = self._cached_preferred_size[0] - self.hpadding if "v" in self.fit_components: size[1] = self._cached_preferred_size[1] - self.vpadding ndx = self.stack_index other_ndx = 1 - ndx other_dim = self.other_dimension # Assign sizes of non-resizable components, and compute the total size # used by them (along the stack dimension). total_fixed_size = 0 resizable_components = [] size_prefs = {} total_resizable_size = 0 for component in components: if not self._should_layout(component): continue if self.stack_dimension not in component.resizable: total_fixed_size += component.outer_bounds[ndx] else: preferred_size = component.get_preferred_size() size_prefs[component] = preferred_size total_resizable_size += preferred_size[ndx] resizable_components.append(component) new_bounds_dict = {} # Assign sizes of all the resizable components along the stack dimension if resizable_components: space = self.spacing * (len(self.components) - 1) avail_size = size[ndx] - total_fixed_size - space if total_resizable_size > 0: scale = avail_size / float(total_resizable_size) for component in resizable_components: tmp = list(component.outer_bounds) tmp[ndx] = int(size_prefs[component][ndx] * scale) new_bounds_dict[component] = tmp else: each_size = int(avail_size / len(resizable_components)) for component in resizable_components: tmp = list(component.outer_bounds) tmp[ndx] = each_size new_bounds_dict[component] = tmp # Loop over all the components, assigning position and computing the # size in the other dimension and its position. cur_pos = 0 for component in components: if not self._should_layout(component): continue position = list(component.outer_position) position[ndx] = cur_pos bounds = new_bounds_dict.get(component, list(component.outer_bounds)) cur_pos += bounds[ndx] + self.spacing if (bounds[other_ndx] > size[other_ndx]) or \ (other_dim in component.resizable): # If the component is resizable in the other dimension or it exceeds the # container bounds, set it to the maximum size of the container #component.set_outer_position(other_ndx, 0) #component.set_outer_bounds(other_ndx, size[other_ndx]) position[other_ndx] = 0 bounds[other_ndx] = size[other_ndx] else: #component.set_outer_position(other_ndx, 0) #old_coord = component.outer_position[other_ndx] position[other_ndx] = 0 if align == "min": pass elif align == "max": position[other_ndx] = size[other_ndx] - bounds[other_ndx] elif align == "center": position[other_ndx] = (size[other_ndx] - bounds[other_ndx]) / 2.0 component.outer_position = position component.outer_bounds = bounds component.do_layout() return ### Persistence ########################################################### # PICKLE FIXME: blocked with _pickles, but not sure that was correct. def __getstate__(self): state = super(StackedPlotContainer,self).__getstate__() for key in ['stack_dimension', 'other_dimension', 'stack_index']: if state.has_key(key): del state[key] return state class HPlotContainer(StackedPlotContainer): """ A plot container that stacks all of its components horizontally. Resizable components share the free space evenly. All components are stacked from according to **stack_order* in the same order that they appear in the **components** list. """ draw_order = Instance(list, args=(DEFAULT_DRAWING_ORDER,)) # The order in which components in the plot container are laid out. stack_order = Enum("left_to_right", "right_to_left") # The amount of space to put between components. spacing = Float(0.0) # The vertical alignment of objects that don't span the full height. valign = Enum("bottom", "top", "center") _cached_preferred_size = Tuple def _do_layout(self): """ Actually performs a layout (called by do_layout()). """ if self.stack_order == "left_to_right": components = self.components else: components = self.components[::-1] if self.valign == "bottom": align = "min" elif self.valign == "center": align = "center" else: align = "max" return self._do_stack_layout(components, align) ### Persistence ########################################################### #_pickles = ("stack_order", "spacing") def __getstate__(self): state = super(HPlotContainer,self).__getstate__() for key in ['_cached_preferred_size']: if state.has_key(key): del state[key] return state class VPlotContainer(StackedPlotContainer): """ A plot container that stacks plot components vertically. """ draw_order = Instance(list, args=(DEFAULT_DRAWING_ORDER,)) # Overrides StackedPlotContainer. stack_dimension = "v" # Overrides StackedPlotContainer. other_dimension = "h" # Overrides StackedPlotContainer. stack_index = 1 # VPlotContainer attributes # The horizontal alignment of objects that don't span the full width. halign = Enum("left", "right", "center") # The order in which components in the plot container are laid out. stack_order = Enum("bottom_to_top", "top_to_bottom") # The amount of space to put between components. spacing = Float(0.0) def _do_layout(self): """ Actually performs a layout (called by do_layout()). """ if self.stack_order == "bottom_to_top": components = self.components else: components = self.components[::-1] if self.halign == "left": align = "min" elif self.halign == "center": align = "center" else: align = "max" return self._do_stack_layout(components, align) class GridPlotContainer(BasePlotContainer): """ A GridPlotContainer consists of rows and columns in a tabular format. Each cell's width is the same as all other cells in its column, and each cell's height is the same as all other cells in its row. Although grid layout requires more layout information than a simple ordered list, this class keeps components as a simple list and exposes a **shape** trait. """ draw_order = Instance(list, args=(DEFAULT_DRAWING_ORDER,)) # The amount of space to put on either side of each component, expressed # as a tuple (h_spacing, v_spacing). spacing = Either(Tuple, List, Array) # The vertical alignment of objects that don't span the full height. valign = Enum("bottom", "top", "center") # The horizontal alignment of objects that don't span the full width. halign = Enum("left", "right", "center") # The shape of this container, i.e, (rows, columns). The items in # **components** are shuffled appropriately to match this # specification. If there are fewer components than cells, the remaining # cells are filled in with spaces. If there are more components than cells, # the remainder wrap onto new rows as appropriate. shape = Trait((0,0), Either(Tuple, List, Array)) # This property exposes the underlying grid structure of the container, # and is the preferred way of setting and reading its contents. # When read, this property returns a Numpy array with dtype=object; values # for setting it can be nested tuples, lists, or 2-D arrays. # The array is in row-major order, so that component_grid[0] is the first # row, and component_grid[:,0] is the first column. The rows are ordered # from top to bottom. component_grid = Property # The internal component grid, in row-major order. This gets updated # when any of the following traits change: shape, components, grid_components _grid = Array _cached_total_size = Any _h_size_prefs = Any _v_size_prefs = Any class SizePrefs(object): """ Object to hold size preferences across spans in a particular dimension. For instance, if SizePrefs is being used for the row axis, then each element in the arrays below express sizing information about the corresponding column. """ # The maximum size of non-resizable elements in the span. If an # element of this array is 0, then its corresponding span had no # non-resizable components. fixed_lengths = Array # The maximum preferred size of resizable elements in the span. # If an element of this array is 0, then its corresponding span # had no resizable components with a non-zero preferred size. resizable_lengths = Array # The direction of resizability associated with this SizePrefs # object. If this SizePrefs is sizing along the X-axis, then # direction should be "h", and correspondingly for the Y-axis. direction = Enum("h", "v") # The index into a size tuple corresponding to our orientation # (0 for horizontal, 1 for vertical). This is derived from # **direction** in the constructor. index = Int(0) def __init__(self, length, direction): """ Initializes this prefs object with empty arrays of the given length and with the given direction. """ self.fixed_lengths = zeros(length) self.resizable_lengths = zeros(length) self.direction = direction if direction == "h": self.index = 0 else: self.index = 1 return def update_from_component(self, component, index): """ Given a component at a particular index along this SizePref's axis, integrates the component's resizability and sizing information into self.fixed_lengths and self.resizable_lengths. """ resizable = self.direction in component.resizable pref_size = component.get_preferred_size() self.update_from_pref_size(pref_size[self.index], index, resizable) def update_from_pref_size(self, pref_length, index, resizable): if resizable: if pref_length > self.resizable_lengths[index]: self.resizable_lengths[index] = pref_length else: if pref_length > self.fixed_lengths[index]: self.fixed_lengths[index] = pref_length return def get_preferred_size(self): return amax((self.fixed_lengths, self.resizable_lengths), axis=0) def compute_size_array(self, size): """ Given a length along the axis corresponding to this SizePref, returns an array of lengths to assign each cell, taking into account resizability and preferred sizes. """ # There are three basic cases for each column: # 1. size < total fixed size # 2. total fixed size < size < fixed size + resizable preferred size # 3. fixed size + resizable preferred size < size # # In all cases, non-resizable components get their full width. # # For resizable components with non-zero preferred size, the following # actions are taken depending on case: # case 1: They get sized to 0. # case 2: They get a fraction of their preferred size, scaled based on # the amount of remaining space after non-resizable components # get their full size. # case 3: They get their full preferred size. # # For resizable components with no preferred size (indicated in our scheme # by having a preferred size of 0), the following actions are taken # depending on case: # case 1: They get sized to 0. # case 2: They get sized to 0. # case 3: All resizable components with no preferred size split the # remaining space evenly, after fixed width and resizable # components with preferred size get their full size. fixed_lengths = self.fixed_lengths resizable_lengths = self.resizable_lengths return_lengths = zeros_like(fixed_lengths) fixed_size = sum(fixed_lengths) fixed_length_indices = fixed_lengths > resizable_lengths resizable_indices = resizable_lengths > fixed_lengths fully_resizable_indices = (resizable_lengths + fixed_lengths == 0) preferred_size = sum(fixed_lengths[fixed_length_indices]) + \ sum(resizable_lengths[~fixed_length_indices]) # Regardless of the relationship between available space and # resizable preferred sizes, columns/rows where the non-resizable # component is largest will always get that amount of space. return_lengths[fixed_length_indices] = fixed_lengths[fixed_length_indices] if size <= fixed_size: # We don't use fixed_length_indices here because that mask is # just where non-resizable components were larger than resizable # ones. If our allotted size is less than the total fixed size, # then we should give all non-resizable components their desired # size. indices = fixed_lengths > 0 return_lengths[indices] = fixed_lengths[indices] return_lengths[~indices] = 0 elif size > fixed_size and (fixed_lengths > resizable_lengths).all(): # If we only have to consider non-resizable lengths, and we have # extra space available, then we need to give each column an # amount of extra space corresponding to its size. desired_space = sum(fixed_lengths) if desired_space > 0: scale = size / desired_space return_lengths = (fixed_lengths * scale).astype(int) elif size <= preferred_size or not fully_resizable_indices.any(): # If we don't have enough room to give all the non-fully resizable # components their preferred size, or we have more than enough # room for them and no fully resizable components to take up # the extra space, then we just scale the resizable components # up or down based on the amount of extra space available. delta_lengths = resizable_lengths[resizable_indices] - \ fixed_lengths[resizable_indices] desired_space = sum(delta_lengths) if desired_space > 0: avail_space = size - sum(fixed_lengths) #[fixed_length_indices]) scale = avail_space / desired_space return_lengths[resizable_indices] = (fixed_lengths[resizable_indices] + \ scale * delta_lengths).astype(int) elif fully_resizable_indices.any(): # We have enough room to fit all the non-resizable components # as well as components with preferred sizes, and room left # over for the fully resizable components. Give the resizable # components their desired amount of space, and then give the # remaining space to the fully resizable components. return_lengths[resizable_indices] = resizable_lengths[resizable_indices] avail_space = size - preferred_size count = sum(fully_resizable_indices) space = avail_space / count return_lengths[fully_resizable_indices] = space else: raise RuntimeError("Unhandled sizing case in GridContainer") return return_lengths def get_preferred_size(self, components=None): """ Returns the size (width,height) that is preferred for this component. Overrides PlotComponent. """ if self.fixed_preferred_size is not None: return self.fixed_preferred_size if components is None: components = self.component_grid else: # Convert to array; hopefully it is a list or tuple of list/tuples components = array(components) # These arrays track the maximum widths in each column and maximum # height in each row. numrows, numcols = self.shape no_visible_components = True self._h_size_prefs = GridPlotContainer.SizePrefs(numcols, "h") self._v_size_prefs = GridPlotContainer.SizePrefs(numrows, "v") self._pref_size_cache = {} for i, row in enumerate(components): for j, component in enumerate(row): if not self._should_layout(component): continue else: no_visible_components = False self._h_size_prefs.update_from_component(component, j) self._v_size_prefs.update_from_component(component, i) total_width = sum(self._h_size_prefs.get_preferred_size()) + self.hpadding total_height = sum(self._v_size_prefs.get_preferred_size()) + self.vpadding total_size = array([total_width, total_height]) # Account for spacing. There are N+1 of spaces, where N is the size in # each dimension. if self.spacing is None: spacing = zeros(2) else: spacing = array(self.spacing) total_spacing = array(components.shape[::-1]) * spacing * 2 * (total_size>0) total_size += total_spacing for orientation, ndx in (("h", 0), ("v", 1)): if (orientation not in self.resizable) and \ (orientation not in self.fit_components): total_size[ndx] = self.outer_bounds[ndx] elif no_visible_components or (total_size[ndx] == 0): total_size[ndx] = self.default_size[ndx] self._cached_total_size = total_size if self.resizable == "": return self.outer_bounds else: return self._cached_total_size def _do_layout(self): # If we don't have cached size_prefs, then we need to call # get_preferred_size to build them. if self._cached_total_size is None: self.get_preferred_size() # If we need to fit our components, then rather than using our # currently assigned size to do layout, we use the preferred # size we computed from our components. size = array(self.bounds) if self.fit_components != "": self.get_preferred_size() if "h" in self.fit_components: size[0] = self._cached_total_size[0] - self.hpadding if "v" in self.fit_components: size[1] = self._cached_total_size[1] - self.vpadding # Compute total_spacing and spacing, which are used in computing # the bounds and positions of all the components. shape = array(self._grid.shape).transpose() if self.spacing is None: spacing = array([0,0]) else: spacing = array(self.spacing) total_spacing = spacing * 2 * shape # Compute the total space used by non-resizable and resizable components # with non-zero preferred sizes. widths = self._h_size_prefs.compute_size_array(size[0] - total_spacing[0]) heights = self._v_size_prefs.compute_size_array(size[1] - total_spacing[1]) # Set the baseline h and v positions for each cell. Resizable components # will get these as their position, but non-resizable components will have # to be aligned in H and V. summed_widths = cumsum(hstack(([0], widths[:-1]))) summed_heights = cumsum(hstack(([0], heights[-1:0:-1]))) h_positions = (2*(arange(self._grid.shape[1])+1) - 1) * spacing[0] + summed_widths v_positions = (2*(arange(self._grid.shape[0])+1) - 1) * spacing[1] + summed_heights v_positions = v_positions[::-1] # Loop over all rows and columns, assigning position, setting bounds for # resizable components, and aligning non-resizable ones valign = self.valign halign = self.halign for j, row in enumerate(self._grid): for i, component in enumerate(row): if not self._should_layout(component): continue r = component.resizable x = h_positions[i] y = v_positions[j] w = widths[i] h = heights[j] if "v" not in r: # Component is not vertically resizable if valign == "top": y += h - component.outer_height elif valign == "center": y += (h - component.outer_height) / 2 if "h" not in r: # Component is not horizontally resizable if halign == "right": x += w - component.outer_width elif halign == "center": x += (w - component.outer_width) / 2 component.outer_position = [x,y] bounds = list(component.outer_bounds) if "h" in r: bounds[0] = w if "v" in r: bounds[1] = h component.outer_bounds = bounds component.do_layout() return def _reflow_layout(self): """ Re-computes self._grid based on self.components and self.shape. Adjusts self.shape accordingly. """ numcells = self.shape[0] * self.shape[1] if numcells < len(self.components): numrows, numcols = divmod(len(self.components), self.shape[0]) self.shape = (numrows, numcols) grid = array(self.components, dtype=object) grid.resize(self.shape) grid[grid==0] = None self._grid = grid self._layout_needed = True return def _shape_changed(self, old, new): self._reflow_layout() def __components_changed(self, old, new): self._reflow_layout() def __components_items_changed(self, event): self._reflow_layout() def _get_component_grid(self): return self._grid def _set_component_grid(self, val): grid = array(val) grid_set = set(grid.flatten()) # Figure out which of the components in the component_grid are new, # and which have been removed. existing = set(array(self._grid).flatten()) new = grid_set - existing removed = existing - grid_set for component in removed: if component is not None: component.container = None for component in new: if component is not None: if component.container is not None: component.container.remove(component) component.container = self self.set(shape=grid.shape, trait_change_notify=False) self._components = list(grid.flatten()) if self._should_compact(): self.compact() self.invalidate_draw() return ### EOF chaco-4.5.0/chaco/plot_factory.py0000644000076600000240000002206212426452312017514 0ustar jrocherstaff00000000000000""" Contains convenience functions to create ready-made PlotRenderer and PlotFrame instances of various types. """ from numpy import array, ndarray, transpose, cos, sin # Local relative imports from abstract_data_source import AbstractDataSource from array_data_source import ArrayDataSource from axis import PlotAxis from barplot import BarPlot from data_range_1d import DataRange1D from grid import PlotGrid from linear_mapper import LinearMapper from scatterplot import ScatterPlot from polar_mapper import PolarMapper from lineplot import LinePlot from polar_line_renderer import PolarLineRenderer def _create_data_sources(data, index_sort="none"): """ Returns datasources for index and value based on the inputs. Assumes that the index data is unsorted unless otherwise specified. """ if (type(data) == ndarray) or (len(data) == 2): index, value = data if type(index) in (list, tuple, ndarray): index = ArrayDataSource(array(index), sort_order=index_sort) elif not isinstance(index, AbstractDataSource): raise RuntimeError, "Need an array or list of values or a DataSource, got %s instead." % type(index) if type(value) in (list, tuple, ndarray): value = ArrayDataSource(array(value)) elif not isinstance(value, AbstractDataSource): raise RuntimeError, "Need an array or list of values or a DataSource, got %s instead." % type(index) return index, value else: raise RuntimeError, "Unable to create datasources." def create_scatter_plot(data=[], index_bounds=None, value_bounds=None, orientation="h", color="green", marker="square", marker_size=4, bgcolor="transparent", outline_color="black", border_visible=True, add_grid=False, add_axis=False, index_sort="none"): """ Creates a ScatterPlot from a single Nx2 data array or a tuple of two length-N 1-D arrays. The data must be sorted on the index if any reverse-mapping tools are to be used. Pre-existing "index" and "value" datasources can be passed in. """ index, value = _create_data_sources(data) if index_bounds is not None: index_range = DataRange1D(low=index_bounds[0], high=index_bounds[1]) else: index_range = DataRange1D() index_range.add(index) index_mapper = LinearMapper(range=index_range) if value_bounds is not None: value_range = DataRange1D(low=value_bounds[0], high=value_bounds[1]) else: value_range = DataRange1D() value_range.add(value) value_mapper = LinearMapper(range=value_range) plot = ScatterPlot(index=index, value=value, index_mapper=index_mapper, value_mapper=value_mapper, orientation=orientation, marker=marker, marker_size=marker_size, color=color, bgcolor=bgcolor, outline_color=outline_color, border_visible=border_visible,) if add_grid: add_default_grids(plot, orientation) if add_axis: add_default_axes(plot, orientation) return plot def create_line_plot(data=[], index_bounds=None, value_bounds=None, orientation="h", color="red", width=1.0, dash="solid", value_mapper_class=LinearMapper, bgcolor="transparent", border_visible=False, add_grid=False, add_axis=False, index_sort="none"): index, value = _create_data_sources(data, index_sort) if index_bounds is not None: index_range = DataRange1D(low=index_bounds[0], high=index_bounds[1]) else: index_range = DataRange1D() index_range.add(index) index_mapper = LinearMapper(range=index_range) if value_bounds is not None: value_range = DataRange1D(low=value_bounds[0], high=value_bounds[1]) else: value_range = DataRange1D() value_range.add(value) value_mapper = value_mapper_class(range=value_range) plot = LinePlot(index=index, value=value, index_mapper = index_mapper, value_mapper = value_mapper, orientation = orientation, color = color, bgcolor = bgcolor, line_width = width, line_style = dash, border_visible=border_visible) if add_grid: add_default_grids(plot, orientation) if add_axis: add_default_axes(plot, orientation) return plot def create_bar_plot(data=[], index_bounds=None, value_bounds=None, orientation="h", color="red", bar_width=10.0, value_mapper_class=LinearMapper, line_color="black", fill_color="red", line_width=1, bgcolor="transparent", border_visible=False, antialias=True, add_grid=False, add_axis=False): index, value = _create_data_sources(data) if index_bounds is not None: index_range = DataRange1D(low=index_bounds[0], high=index_bounds[1]) else: index_range = DataRange1D() index_range.add(index) index_mapper = LinearMapper(range=index_range) if value_bounds is not None: value_range = DataRange1D(low=value_bounds[0], high=value_bounds[1]) else: value_range = DataRange1D() value_range.add(value) value_mapper = value_mapper_class(range=value_range) # Create the plot plot = BarPlot(index=index, value=value, value_mapper=value_mapper, index_mapper=index_mapper, orientation=orientation, line_color=line_color, fill_color=fill_color, line_width=line_width, bar_width=bar_width, antialias=antialias,) if add_grid: add_default_grids(plot, orientation) if add_axis: add_default_axes(plot, orientation) return plot def create_polar_plot(data, orientation='h', color='black', width=1.0, dash="solid", grid="dot", value_mapper_class=PolarMapper): if (type(data) != ndarray) and (len(data) == 2): data = transpose(array(data)) r_data, t_data = transpose(data) index_data= r_data*cos(t_data) value_data= r_data*sin(t_data) index = ArrayDataSource(index_data, sort_order='ascending') # Typically the value data is unsorted value = ArrayDataSource(value_data) index_range = DataRange1D() index_range.add(index) index_mapper = PolarMapper(range=index_range) value_range = DataRange1D() value_range.add(value) value_mapper = value_mapper_class(range=value_range) plot = PolarLineRenderer(index=index, value=value, index_mapper = index_mapper, value_mapper = value_mapper, orientation = orientation, color = color, line_width = width, line_style = dash, grid_style = grid) return plot def add_default_axes(plot, orientation="normal", vtitle="", htitle="", axis_class=PlotAxis): """ Creates left and bottom axes for a plot. Assumes that the index is horizontal and value is vertical by default; set *orientation* to something other than "normal" if they are flipped. """ if orientation in ("normal", "h"): v_mapper = plot.value_mapper h_mapper = plot.index_mapper else: v_mapper = plot.index_mapper h_mapper = plot.value_mapper left = axis_class(orientation='left', title= vtitle, mapper=v_mapper, component=plot) bottom = axis_class(orientation='bottom', title= htitle, mapper=h_mapper, component=plot) plot.underlays.append(left) plot.underlays.append(bottom) return left, bottom def add_default_grids(plot, orientation="normal"): """ Creates horizontal and vertical gridlines for a plot. Assumes that the index is horizontal and value is vertical by default; set orientation to something other than "normal" if they are flipped. """ if orientation in ("normal", "h"): v_mapper = plot.index_mapper h_mapper = plot.value_mapper else: v_mapper = plot.value_mapper h_mapper = plot.index_mapper vgrid = PlotGrid(mapper=v_mapper, orientation='vertical', component=plot, line_color="lightgray", line_style="dot") hgrid = PlotGrid(mapper=h_mapper, orientation='horizontal', component=plot, line_color="lightgray", line_style="dot") plot.underlays.append(vgrid) plot.underlays.append(hgrid) return hgrid, vgrid # EOF chaco-4.5.0/chaco/plot_graphics_context.py0000644000076600000240000000510612426452312021411 0ustar jrocherstaff00000000000000""" Defines the PlotGraphicsContext class. """ from __future__ import with_statement from enable.kiva_graphics_context import GraphicsContext class PlotGraphicsContextMixin(object): """ A Kiva graphics context, which facilitates rendering plots and plot components into an offscreen or memory buffer. Its only real difference from a Kiva graphics context is that this class correctly offsets the coordinate frame by (0.5, 0.5) and increases the actual size of the image by 1 pixel in each dimension. When rendering into on-screen windows through Enable, this transformation step is handled by Enable. """ # FIXME: Right now this does not resize correctly. (But you shouldn't # resize your GC, anyway!) def __init__(self, size_or_ary, *args, **kw): scale = kw.pop('dpi', 72.0) / 72.0 if type(size_or_ary) in (list, tuple) and len(size_or_ary) == 2: size_or_ary = (size_or_ary[0]*scale + 1, size_or_ary[1]*scale + 1) super(PlotGraphicsContextMixin, self).__init__(size_or_ary, *args, **kw) self.translate_ctm(0.5, 0.5) self.scale_ctm(scale, scale) return def render_component(self, component, container_coords=False): """ Renders the given component. Parameters ---------- component : Component The component to be rendered. container_coords : Boolean Whether to use coordinates of the component's container Description ----------- If *container_coords* is False, then the (0,0) coordinate of this graphics context corresponds to the lower-left corner of the component's **outer_bounds**. If *container_coords* is True, then the method draws the component as it appears inside its container, i.e., it treats (0,0) of the graphics context as the lower-left corner of the container's outer bounds. """ x, y = component.outer_position if not container_coords: x = -x y = -y with self: self.translate_ctm(x, y) component.draw(self, view_bounds=(0, 0, self.width(), self.height())) return def clip_to_rect(self, x, y, width, height): """ Offsets the coordinate frame by (0.5, 0.5) and increases the actual size of the image by 1 pixel in each dimension. Overrides Kiva GraphicsContext. """ super(PlotGraphicsContextMixin, self).clip_to_rect(x-0.5, y-0.5, width+1, height+1) class PlotGraphicsContext(PlotGraphicsContextMixin, GraphicsContext): pass chaco-4.5.0/chaco/plot_label.py0000644000076600000240000001753712426452312017137 0ustar jrocherstaff00000000000000""" Defines the PlotLabel class. """ from __future__ import with_statement from enable.font_metrics_provider import font_metrics_provider from traits.api import DelegatesTo, Enum, Instance, Str, Trait from abstract_overlay import AbstractOverlay from label import Label LabelDelegate = DelegatesTo("_label") class PlotLabel(AbstractOverlay): """ A label used by plots. This class wraps a simple Label instance, and delegates some traits to it. """ # The text of the label. text = LabelDelegate # The color of the label text. color = DelegatesTo("_label") # The font for the label text. font = LabelDelegate # The angle of rotation of the label. angle = DelegatesTo("_label", "rotate_angle") bgcolor = LabelDelegate border_width = LabelDelegate border_color = LabelDelegate border_visible = LabelDelegate margin = LabelDelegate line_spacing = LabelDelegate #------------------------------------------------------------------------ # Layout-related traits #------------------------------------------------------------------------ # Horizontal justification used if the label has more horizontal space # than it needs. hjustify = Enum("center", "left", "right") # Vertical justification used if the label has more vertical space than it # needs. vjustify = Enum("center", "bottom", "top") # The position of this label relative to the object it is overlaying. # Can be "top", "left", "right", "bottom", and optionally can be preceeded # by the words "inside" or "outside", separated by a space. If "inside" # and "outside" are not provided, then defaults to "outside". # Examples: # inside top # outside right overlay_position = Trait("outside top", Str, None) # Should this PlotLabel modify the padding on its underlying component # if there is not enough room to lay out the text? # FIXME: This could cause cycles in layout, so not implemented for now #modify_component = Bool(True) # By default, this acts like a component and will render on the main # "plot" layer unless its **component** attribute gets set. draw_layer = "plot" #------------------------------------------------------------------------ # Private traits #------------------------------------------------------------------------ # The label has a fixed height and can be resized horizontally. (Overrides # PlotComponent.) resizable = "h" # The Label instance this plot label is wrapping. _label = Instance(Label, args=()) def __init__(self, text="", *args, **kw): super(PlotLabel, self).__init__(*args, **kw) self.text = text return def overlay(self, component, gc, view_bounds=None, mode="normal"): """ Draws this label overlaid on another component. Overrides AbstractOverlay. """ self._draw_overlay(gc, view_bounds, mode) return def get_preferred_size(self): """ Returns the label's preferred size. Overrides PlotComponent. """ dummy_gc = font_metrics_provider() size = self._label.get_bounding_box(dummy_gc) return size def do_layout(self): """ Tells this component to do layout. Overrides PlotComponent. """ if self.component is not None: self._layout_as_overlay() else: self._layout_as_component() return def _draw_overlay(self, gc, view_bounds=None, mode="normal"): """ Draws the overlay layer of a component. Overrides PlotComponent. """ # Perform justification and compute the correct offsets for # the label position width, height = self._label.get_bounding_box(gc) if self.hjustify == "left": x_offset = 0 elif self.hjustify == "right": x_offset = self.width - width elif self.hjustify == "center": x_offset = int((self.width - width) / 2) if self.vjustify == "bottom": y_offset = 0 elif self.vjustify == "top": y_offset = self.height - height elif self.vjustify == "center": y_offset = int((self.height - height) / 2) with gc: # XXX: Uncomment this after we fix kiva GL backend's clip stack #gc.clip_to_rect(self.x, self.y, self.width, self.height) # We have to translate to our position because the label # tries to draw at (0,0). gc.translate_ctm(self.x + x_offset, self.y + y_offset) self._label.draw(gc) return def _draw_plot(self, gc, view_bounds=None, mode="normal"): if self.component is None: # We are not overlaying anything else, so we should render # on this layer self._draw_overlay(gc, view_bounds, mode) def _layout_as_component(self, size=None, force=False): pass def _layout_as_overlay(self, size=None, force=False): """ Lays out the label as an overlay on another component. """ if self.component is not None: orientation = self.overlay_position outside = True if "inside" in orientation: tmp = orientation.split() tmp.remove("inside") orientation = tmp[0] outside = False elif "outside" in orientation: tmp = orientation.split() tmp.remove("outside") orientation = tmp[0] if orientation in ("left", "right"): self.y = self.component.y self.height = self.component.height if not outside: gc = font_metrics_provider() self.width = self._label.get_bounding_box(gc)[0] if orientation == "left": if outside: self.x = self.component.outer_x self.width = self.component.padding_left else: self.outer_x = self.component.x elif orientation == "right": if outside: self.x = self.component.x2 + 1 self.width = self.component.padding_right else: self.x = self.component.x2 - self.outer_width elif orientation in ("bottom", "top"): self.x = self.component.x self.width = self.component.width if not outside: gc = font_metrics_provider() self.height = self._label.get_bounding_box(gc)[1] if orientation == "bottom": if outside: self.y = self.component.outer_y self.height = self.component.padding_bottom else: self.outer_y = self.component.y elif orientation == "top": if outside: self.y = self.component.y2 + 1 self.height = self.component.padding_top else: self.y = self.component.y2 - self.outer_height else: # Leave the position alone pass return def _text_changed(self, old, new): self._label.text = new self.do_layout() return def _font_changed(self, old, new): self._label.font = new self.do_layout() return def _angle_changed(self, old, new): self._label.rotate_angle = new self.do_layout() return def _overlay_position_changed(self): self.do_layout() def _component_changed(self, old, new): if new: self.draw_layer = "overlay" else: self.draw_layer = "plot" return # EOF chaco-4.5.0/chaco/plot_template.py0000644000076600000240000001553412426452312017666 0ustar jrocherstaff00000000000000""" Defines classes needed to support templates in Chaco. * AbstractTemplatizer * CodeMetadata * CodeTemplate * PlotTemplate * PlotTemplateException * TemplateDescriptor * Templatizable Also defines the convenience function:: bind_template(template, vars, type_check=False) """ from traits.api import Bool, Dict, HasTraits, Instance, Str def bind_template(template, vars, type_check=False): """ A convenience method for binding a plot template to set of variables. Parameters ---------- template : a list of strings The code for the template, or a PlotTemplate object. vars : a dict Maps a template variable name to an object. type_check : Boolean If True, raises a PlotTemplateException with the name of the variable in *vars* whose type does not match what is specified in the template. """ return class PlotTemplateException(Exception): """ Raised for errors in plot templates. """ pass class TemplateDescriptor(HasTraits): """ Describes the names and types of template variables for a template. """ # A dict with the template variable names as keys. If the template is # unbound, the values are string representations of the types of objects # expected for each key. These are simple types, e.g., 'tuple' or # 'PlotAxis'. If the template is bound, then the values are object # references. vars = Dict class Templatizable(HasTraits): """ Mix-in class that makes objects capable of being incorporated into a Chaco template. Primarily defines the protocol used to query the class for its contents. """ def templatize(self, my_name, ): """ Returns a dict mapping the name of the child in the local name space to a Templatizable object reference. """ raise NotImplementedError def __gettemplate__(self): """ Returns a templatized version of the object. """ # def bind(self, def rebind(self, obj): """ Replaces this object with the state in peer object *obj*. This method allows PlotTemplates to be used as live, application-level templates and not merely as a means to generating a plot script. (If you are having trouble implementing this method for something that should be Templatizable, it's probably a sign that you haven't fully considered the implications of your design, or that you are doing something a little weird.) """ raise NotImplementedError class PlotTemplate(HasTraits): """ Abstract base class for plot templates. """ pass class AbstractTemplatizer(HasTraits): """ A Templatizer accepts any subclass of Templatizable and returns a PlotTemplate. """ class CodeMetadata(HasTraits): """ Represents all the metadata about a plot template, to be stored into the generated code. The generated code for a plot template must create one of these objects, which is then used to drive the loading of the rest of the template. """ # Not used for now, but could be handled later. version = "1.0" # The name of the Chaco package. pkg_name = "chaco" # A dict with the template variable names as keys. If the template is # unbound, the values are string representations of the types of objects # expected for each key. These are simple types, e.g., 'tuple' or 'PlotAxis'. # If the template is bound, then the values are object references. template_vars = Dict # The name to evaluate to get the root object when the template # is instantiated. Defaults to "ROOT_OBJ", but you can customize # this for aesthetic or readability reasons. root_name = Str class CodeTemplate(PlotTemplate): """ A Chaco plot template. Because Chaco plot templates are just executable code that produces Chaco plots, the PlotTemplate class is used to manage, interact with, and inspect the code for the template. """ #------------------------------------------------------------------------- # Configuration and general state of the template #------------------------------------------------------------------------- # Is the template completely bound? is_bound = Bool(False) #------------------------------------------------------------------------- # Object graph traits #------------------------------------------------------------------------- # The top-level Templatizable component in the plot. root = Instance(Templatizable) #------------------------------------------------------------------------- # Code-related traits # These are used during the actual code generation process. #------------------------------------------------------------------------- # Global variables used during the code generation process. code_globals = Dict # Imports used during the code generation process. code_imports = Dict def create_strings(self): """ Returns a list of strings which can be passed to bind_template(). """ # TODO: do we need this?? can we do live generation?!!! pass def load_from_strings(self, stringlist): """ Fills this plot template with the template in *stringlist*. NOTE: Don't use this to bind a template to data! There is a much easier way to do that: use the bind_template() function. """ pass #------------------------------------------------------------------------- # Private methods #------------------------------------------------------------------------- def _write_metadata(self): """ Produces a list of strings representing the code to create the metadata portion of this PlotTemplate. """ pass #------------------------------------------------------------------------- # Methods used by Templatizable objects to query the generator about the # state of code generation. #------------------------------------------------------------------------- def create_global(self, name_hint): """ Requests that a new global symbol be allocated with the given name. Returns the actual name that was created. """ pass def create_import(self, import_line, ): """ Adds another import line, verbatim, to the top of the output code. No order of imports is guaranteed. """ pass def create_template_var(self, name_hint): """ Creates a new variable for parameterizing the template. Returns a string that represents the special token to be used in the output code to signal where the template value can be bound. """ pass def create_function(self, name_hint): """ Requests that a new function be allocated with the given name. Returns the actual name that was created. """ pass # EOF chaco-4.5.0/chaco/plotscrollbar.py0000644000076600000240000002076612426452312017702 0ustar jrocherstaff00000000000000 from traits.api import Any, Enum, Int, Property, Trait from enable.api import NativeScrollBar class PlotScrollBar(NativeScrollBar): """ A ScrollBar that can be wired up to anything with an xrange or yrange and which can be attached to a plot container. """ # The axis corresponding to this scrollbar. axis = Enum("index", "value") # The renderer or Plot to attach this scrollbar to. By default, this # is just self.component. plot = Property # The mapper for associated with the scrollbar. By default, this is the # mapper on **plot** that corresponds to **axis**. mapper = Property #------------------------------------------------------------------------ # Private traits #------------------------------------------------------------------------ # The value of the override plot to use, if any. If None, then uses # self.component. _plot = Trait(None, Any) # The value of the override mapper to use, if any. If None, then uses the # mapper on self.component. _mapper = Trait(None, Any) # Stores the index (0 or 1) corresponding to self.axis _axis_index = Trait(None, None, Int) #---------------------------------------------------------------------- # Public methods #---------------------------------------------------------------------- def force_data_update(self): """ This forces the scrollbar to recompute its range bounds. This should be used if datasources are changed out on the range, or if the data ranges on existing datasources of the range are changed. """ self._handle_dataspace_update() def overlay(self, component, gc, view_bounds=None, mode="default"): self.do_layout() self._draw_mainlayer(gc, view_bounds, "default") def _draw_plot(self, gc, view_bounds=None, mode="default"): self._draw_mainlayer(gc, view_bounds, "default") def _do_layout(self): if getattr(self.plot, "_layout_needed", False): self.plot.do_layout() axis = self._determine_axis() low, high = self.mapper.screen_bounds self.bounds[axis] = high - low self.position[axis] = low self._widget_moved = True def _get_abs_coords(self, x, y): if self.container is not None: return self.container.get_absolute_coords(x, y) else: return self.component.get_absolute_coords(x, y) #---------------------------------------------------------------------- # Scrollbar #---------------------------------------------------------------------- def _handle_dataspace_update(self): # This method reponds to changes from the dataspace side, e.g. # a change in the range bounds or the data bounds of the datasource. # Get the current datasource bounds range = self.mapper.range bounds_list = [source.get_bounds() for source in range.sources \ if source.get_size() > 0] mins, maxes = zip(*bounds_list) dmin = min(mins) dmax = max(maxes) #import pdb; pdb.set_trace() view = float(range.high - range.low) # Take into account the range's current low/high and the data bounds # to compute the total range totalmin = min(range.low, dmin) totalmax = max(range.high, dmax) # Compute the size available for the scrollbar to scroll in scrollrange = (totalmax - totalmin) - view if round(scrollrange/20.0) > 0.0: ticksize = scrollrange / round(scrollrange/20.0) else: ticksize = 1 foo = (totalmin, totalmax, view, ticksize) print "scrollrange:", foo self.set(range = foo, scroll_position = max(min(self.scroll_position, totalmax-view), totalmin), trait_change_notify=False) self._scroll_updated = True self.request_redraw() return def _scroll_position_changed(self): super(PlotScrollBar, self)._scroll_position_changed() # Notify our range that we've changed range = self.mapper.range view_width = range.high - range.low new_scroll_pos = self.scroll_position range.set_bounds(new_scroll_pos, new_scroll_pos + view_width) return #---------------------------------------------------------------------- # Event listeners #---------------------------------------------------------------------- def _component_changed(self, old, new): # Check to see if we're currently overriding the value of self.component # in self.plot. If so, then don't change the event listeners. if self._plot is not None: return if old is not None: self._modify_plot_listeners(old, "detach") if new is not None: self._modify_plot_listeners(new, "attach") self._update_mapper_listeners() return def __plot_changed(self, old, new): if old is not None: self._modify_plot_listeners(old, "detach") elif self.component is not None: # Remove listeners from self.component, if it exists self._modify_plot_listeners(self.component, "detach") if new is not None: self._modify_plot_listeners(new, "attach") self._update_mapper_listeners() elif self.component is not None: self._modify_plot_listeners(self.component, "attach") self._update_mapper_listeners() return def _modify_plot_listeners(self, plot, action="attach"): if action == "attach": remove=False else: remove=True plot.on_trait_change(self._component_bounds_handler, "bounds", remove=remove) plot.on_trait_change(self._component_bounds_handler, "bounds_items", remove=remove) plot.on_trait_change(self._component_pos_handler, "position", remove=remove) plot.on_trait_change(self._component_pos_handler, "position_items", remove=remove) return def _component_bounds_handler(self): self._handle_dataspace_update() self._widget_moved = True def _component_pos_handler(self): self._handle_dataspace_update() self._widget_moved = True def _update_mapper_listeners(self): #if self._mapper pass def _handle_mapper_updated(self): self._handle_dataspace_update() #------------------------------------------------------------------------ # Property getter/setters #------------------------------------------------------------------------ def _get_plot(self): if self._plot is not None: return self._plot else: return self.component def _set_plot(self, val): self._plot = val return def _get_mapper(self): if self._mapper is not None: return self._mapper else: return getattr(self.plot, self.axis + "_mapper") def _set_mapper(self, new_mapper): self._mapper = new_mapper return def _get_axis_index(self): if self._axis_index is None: return self._determine_axis() else: return self._axis_index def _set_axis_index(self, val): self._axis_index = val return #------------------------------------------------------------------------ # Private methods #------------------------------------------------------------------------ def _get_axis_coord(self, event, axis="index"): """ Returns the coordinate of the event along the axis of interest to this tool (or along the orthogonal axis, if axis="value"). """ event_pos = (event.x, event.y) if axis == "index": return event_pos[ self.axis_index ] else: return event_pos[ 1 - self.axis_index ] def _determine_axis(self): """ Determines whether the index of the coordinate along this tool's axis of interest is the first or second element of an (x,y) coordinate tuple. This method is only called if self._axis_index hasn't been set (or is None). """ if self.axis == "index": if self.plot.orientation == "h": return 0 else: return 1 else: # self.axis == "value" if self.plot.orientation == "h": return 1 else: return 0 chaco-4.5.0/chaco/plugin/0000755000076600000240000000000012426466422015740 5ustar jrocherstaff00000000000000chaco-4.5.0/chaco/plugin/__init__.py0000644000076600000240000000000012426452312020030 0ustar jrocherstaff00000000000000chaco-4.5.0/chaco/plugin/chaco_plugin.py0000644000076600000240000000307112426452312020737 0ustar jrocherstaff00000000000000""" Envisage 3 plugin for Chaco functionality. """ from envisage.api import Plugin from traits.api import List ID = 'chaco' ICHACO_SESSION = ID + '.plugin.session_service.SessionService' class ChacoPlugin(Plugin): """ Envisage 3 plugin for Chaco functionality. """ id = ID name = 'Chaco plugin' #### Contributions to extension points made by this plugin ################# # Extension point Ids. COMMANDS = 'envisage.plugins.python_shell.commands' contributed_commands = List(contributes_to=COMMANDS) def _contributed_commands_default(self): commands = [ "from chaco.shell.commands import *", ] return commands #### Plugin interface ###################################################### def start(self): """ Monkeypatch the Chaco shell subsystem. """ from chaco import shell from chaco.shell import commands from chaco.plugin.workbench_session import WorkbenchSession commands.session = shell.session = WorkbenchSession( application=self.application) def show(): """ Shows all the figure windows that have been created thus far, and creates a GUI main loop. This function is useful in scripts to show plots and keep their windows open, and has no effect when used from the interpreter prompt. Inside Envisage, just raise the current window. """ win = commands.session.active_window win.raise_window() commands.show = show chaco-4.5.0/chaco/plugin/plot_editor.py0000644000076600000240000000714712426452312020640 0ustar jrocherstaff00000000000000from chaco.shell.scaly_plot import ScalyPlot from enable.component_editor import ComponentEditor from pyface.workbench.api import TraitsUIEditor from traits.api import Any, Enum, HasTraits, Property, Str from traitsui import api as tui class PlotUI(HasTraits): """ Simple Traits UI proxy for a Chaco plot. """ # The plot. component = Any() traits_view = tui.View( tui.Item('component', editor=ComponentEditor(), show_label=False), resizable=True, ) class PlotEditor(TraitsUIEditor): """ A Workbench Editor showing a Chaco plot for the shell interface. """ bgcolor = Str('white') image_default_origin = Enum("bottom left", "top left", "bottom right", "top right") # The plot. component = Property(Any) container = Property(Any) # The PlotData. data = Any() # The PlotSession of which we are a part. We need to know this in order # to notify it of our being closed, etc. session = Any() def __init__(self, is_image=False, bgcolor="white", image_default_origin="top left", *args, **kw): super(TraitsUIEditor, self).__init__(**kw) # Some defaults which should be overridden by preferences. self.bgcolor = bgcolor self.image_default_origin = image_default_origin # Create an empty top-level container if is_image: top_container = self._create_top_img_container() else: top_container = self._create_top_container() self.obj = PlotUI(component=top_container) #### PlotWindow interface ################################################## def get_container(self): return self.obj.component def set_container(self, container): self.obj.component = container def iconize(self, iconize): """Iconizes the window if *iconize* is True. Do nothing in this implementation. """ def maximize(self, maximize): """ If *maximize* is True, maximizes the window size; restores if False. Do nothing in this implementation. """ def set_size(self, width, height): pass def set_title(self, title): self.name = title def raise_window(self): self.window.activate_editor(self) #### Editor interface ###################################################### def destroy_control(self): """ Destroy the toolkit-specific control that represents the part. """ self._on_window_close() super(TraitsUIEditor, self).destroy_control() #### Private interface ##################################################### def _get_container(self): return self.obj.component def _set_container(self, value): self.obj.component = value def _get_component(self): return self.obj.component def _set_component(self, value): self.obj.component = value def _create_top_container(self): plot = ScalyPlot( padding=50, fill_padding=True, bgcolor=self.bgcolor, use_backbuffer=True, ) return plot def _create_top_img_container(self): plot = ScalyPlot( padding=50, fill_padding=True, bgcolor=self.bgcolor, use_backbuffer=True, default_origin=self.image_default_origin, ) return plot def _on_window_close(self): if self.session: try: ndx = self.session.windows.index(self) self.session.del_window(ndx) except ValueError: pass chaco-4.5.0/chaco/plugin/workbench_session.py0000644000076600000240000000324512426452312022034 0ustar jrocherstaff00000000000000""" A Chaco Shell PlotSession which raises Workbench Editors instead of free-standing windows. """ from traits.api import Any, Dict, List, Str from chaco.shell.session import PlotSession from plot_editor import PlotEditor class WorkbenchSession(PlotSession): """ A Chaco Shell PlotSession which raises Workbench Editors instead of free-standing windows. """ # The Envisage Application we are in. application = Any() # The list of currently active windows. windows = List() # A dict mapping names to windows. window_map = Dict(Str, Any) def new_window(self, name=None, title=None, is_image=False): """Creates a new window and returns the index into the **windows** list for the new window. """ workbench = self.application.get_service( 'envisage.ui.workbench.workbench.Workbench') new_win = PlotEditor( is_image=is_image, size=(self.prefs.window_width, self.prefs.window_height), bgcolor=self.prefs.bgcolor, image_default_origin=self.prefs.image_default_origin, window=workbench.active_window, ) new_win.data = self.data new_win.get_container().data = self.data new_win.session = self if title is not None: new_win.set_title(title) elif name != None: new_win.set_title(name) else: new_win.set_title(self.prefs.default_window_name) self.windows.append(new_win) if name != None: self.window_map[name] = new_win workbench.edit(new_win.obj, kind=lambda *args, **kwds: new_win) return len(self.windows)-1 chaco-4.5.0/chaco/point_data_source.py0000644000076600000240000001324312426452312020512 0ustar jrocherstaff00000000000000""" Defines the PointDataSource class. """ # Major library imports from numpy import array, transpose # Enthought library imports from traits.api import Enum, Property, ReadOnly, Tuple # Local, relative imports from base import PointTrait, reverse_map_1d, SortOrderTrait from array_data_source import ArrayDataSource class PointDataSource(ArrayDataSource): """ A data source representing a (possibly unordered) set of (X,Y) points. This is internally always represented by an Nx2 array, so that data[i] refers to a single point (represented as a length-2 array). Most of the traits and methods of ArrayDataSeries work for the PointDataSeries as well, since its data is linear. This class overrides only the methods and traits that are different. """ # The dimensionality of the indices into this data source (overrides # ArrayDataSource). index_dimension = ReadOnly('scalar') # The dimensionality of the value at each index point (overrides # ArrayDataSource). value_dimension = ReadOnly('point') # The sort order of the data. Although sort order is less common with point # data, it can be useful in case where the value data is sorted along some # axis. Note that **sort_index** is used only if **sort_order** is not # 'none'. sort_order = SortOrderTrait # Which of the value axes the **sort_order** refers to. # If **sort_order** is 'none', this attribute is ignored. # In the unlikely event that the value data is sorted along both # X and Y (i.e., monotonic in both axes), then set **sort_index** to # whichever one has the best binary-search performance for hit-testing. sort_index = Enum(0, 1) #------------------------------------------------------------------------ # Private traits #------------------------------------------------------------------------ # The actual data (overrides ArrayDataSource). _data = PointTrait # Cached values of min and max as long as **_data** doesn't change # (overrides ArrayDataSource). ((min_x, max_x), (min_y, max_y)) _cached_bounds = Tuple # These return lists of all the x and y positions, respectively # List of X positions. _xdata = Property # List of Y positions. _ydata = Property #------------------------------------------------------------------------ # AbstractDataSource interface #------------------------------------------------------------------------ def __init__(self, data = transpose(array([[],[]])), **kw): shape = data.shape if (len(shape) != 2) or (shape[1] != 2): raise RuntimeError, "PointDataSource constructor requires Nx2 array, but got array of shape " + str(shape) + " instead." super(PointDataSource, self).__init__(data, **kw) return def get_data(self): """ Returns the data for this data source, or (0.0, 0.0) if it has no data. Overrides ArryDataSource. """ if self._data is not None: return self._data else: return (0.0, 0.0) def reverse_map(self, pt, index=0, outside_returns_none=True): """Returns the index of *pt* in the data source. Overrides ArrayDataSource. Parameters ---------- pt : (x, y) value to find index : 0 or 1 Which of the axes of *pt* the *sort_order* refers to. outside_returns_none : Boolean Whether the method returns None if *pt* is outside the range of the data source; if False, the method returns the value of the bound that *pt* is outside of, in the *index* dimension. """ # reverse_map is of limited utility for a PointDataSeries and thus # we only perform reverse-mapping if the data is sorted along an axis. if self.sort_order == "none": # By default, only provide reverse_map if the value data is sorted # along one of its axes. raise NotImplementedError if index != 0 and index != 1: raise ValueError, "Index must be 0 or 1." # This basically reduces to a scalar data search along self.data[index]. lowerleft, upperright= self._cached_bounds min_val = lowerleft[index] max_val = upperright[index] val = pt[index] if (val < min_val): if outside_returns_none: return None else: return self._min_index elif (val > max_val): if outside_returns_none: return None else: return self._max_index else: return reverse_map_1d(self._data[:,index], val, self.sort_order) #------------------------------------------------------------------------ # Private methods #------------------------------------------------------------------------ def _compute_bounds(self): """ Computes the minimum and maximum values of self._data. Overrides ArrayDataSource. """ if len(self._data) == 0: self._cached_bounds = ((0.0,0.0), (0.0,0.0)) elif len(self._data) == 1: x,y = self._data[0] self._cached_bounds = ((x,y), (x,y)) else: # calculate the X and Y values independently x = self._data[:,0] min_x = min(x) max_x = max(x) y = self._data[:,1] min_y = min(y) max_y = max(y) self._cached_bounds = ((min_x,min_y), (max_x,max_y)) return def _get__xdata(self): return ArrayDataSource(self._data[:,0]) def _get__ydata(self): return ArrayDataSource(self._data[:,1]) # EOF chaco-4.5.0/chaco/polar_line_renderer.py0000644000076600000240000001360712426452312021026 0ustar jrocherstaff00000000000000""" Defines the PolarLineRenderer class. """ from __future__ import with_statement # Major library imports from numpy import array, cos, pi, sin, transpose # Enthought library imports from enable.api import black_color_trait, LineStyle from traits.api import Float # Local, relative imports from abstract_plot_renderer import AbstractPlotRenderer class PolarLineRenderer(AbstractPlotRenderer): """ A renderer for polar line plots. """ #------------------------------------------------------------------------ # Appearance-related traits #------------------------------------------------------------------------ # The color of the origin axis. origin_axis_color_ = (0,0,0,1) # The width of the origin axis. origin_axis_width = 2.0 # The origin axis is visible. origin_axis_visible=True # The grid is visible. grid_visible= True # The orientation of the plot is horizontal; for any other value, it is # transposed orientation = 'h' # The color of the line. color = black_color_trait # The width of the line. line_width = Float(1.0) # The style of the line. line_style = LineStyle("solid") # The style of the grid lines. grid_style= LineStyle("dot") def _gather_points(self): """ Collects the data points that are within the plot bounds and caches them """ # This is just a stub for now. We should really find the lines only # inside the screen range here. x = self.index.get_data() y = self.value.get_data() rad= min(self.width/2.0,self.height/2.0) sx = x*rad+ self.x + self.width/2.0 sy = y*rad+ self.y + self.height/2.0 points = transpose(array((sx,sy))) self._cached_data_pts = points self._cache_valid = True return def _data_changed(self): self._cache_valid = False return def _update_mappers(self): #Dunno if there is anything else to do here self._cache_valid = False def _render(self, gc, points): """ Actually draw the plot. """ with gc: gc.set_antialias(True) self._draw_default_axes(gc) self._draw_default_grid(gc) if len(points)>0: gc.clip_to_rect(self.x, self.y, self.width, self.height) gc.set_stroke_color(self.color_) gc.set_line_width(self.line_width) gc.set_line_dash(self.line_style_) gc.begin_path() gc.lines(points) gc.stroke_path() return def map_screen(self, data_array): """ Maps an array of data points into screen space and returns it as an array. Implements the AbstractPlotRenderer interface. """ if len(data_array) == 0: return [] elif len(data_array) == 1: xtmp, ytmp = transpose(data_array) x_ary = xtmp y_ary = ytmp else: x_ary, y_ary = transpose(data_array) sx = self.index_mapper.map_screen(x_ary) sy = self.value_mapper.map_screen(y_ary) if self.orientation == 'h': return transpose(array((sx, sy))) else: return transpose(array((sy, sx))) def map_data(self, screen_pt): """ Maps a screen space point into the "index" space of the plot. Implements the AbstractPlotRenderer interface. """ if self.orientation == 'h': x, y = screen_pt else: y,x = screen_pt return array((self.index_mapper.map_data(x), self.value_mapper.map_data(y))) def _downsample(self): return self.map_screen(self._cached_data_pts) def _draw_plot(self, *args, **kw): """ Draws the 'plot' layer. """ # Simple compatibility with new-style rendering loop return self._draw_component(*args, **kw) def _draw_component(self, gc, view_bounds=None, mode='normal'): """ Renders the component. """ self._gather_points() self._render(gc, self._cached_data_pts) def _bounds_changed(self, old, new): super(PolarLineRenderer, self)._bounds_changed(old, new) self._update_mappers() def _bounds_items_changed(self, event): super(PolarLineRenderer, self)._bounds_items_changed(event) self._update_mappers() def _draw_default_axes(self, gc): if not self.origin_axis_visible: return with gc: gc.set_stroke_color(self.origin_axis_color_) gc.set_line_width(self.origin_axis_width) gc.set_line_dash(self.grid_style_) x_data,y_data= transpose(self._cached_data_pts) x_center=self.x + self.width/2.0 y_center=self.y + self.height/2.0 for theta in range(12): r= min(self.width/2.0,self.height/2.0) x= r*cos(theta*pi/6) + x_center y= r*sin(theta*pi/6) + y_center data_pts= array([[x_center,y_center],[x,y]]) start,end = data_pts gc.move_to(int(start[0]), int(start[1])) gc.line_to(int(end[0]), int(end[1])) gc.stroke_path() return def _draw_default_grid(self,gc): if not self.grid_visible: return with gc: gc.set_stroke_color(self.origin_axis_color_) gc.set_line_width(self.origin_axis_width) gc.set_line_dash(self.grid_style_) x_data,y_data = transpose(self._cached_data_pts) x_center = self.x + self.width/2.0 y_center = self.y + self.height/2.0 rad = min(self.width/2.0, self.height/2.0) for r_part in range(1,5): r = rad*r_part/4 gc.arc(x_center, y_center, r, 0, 2*pi) gc.stroke_path() return chaco-4.5.0/chaco/polar_mapper.py0000644000076600000240000000574012426452312017474 0ustar jrocherstaff00000000000000""" Defines the PolarMapper class, which maps from a 1-D region in data space into a 1-D output space. """ # Major library imports from numpy import array # Enthought library imports from traits.api import Bool, Float # Local relative imports from abstract_mapper import AbstractMapper ############################################################### # same as linear mapper at the moment... to be modified later # ############################################################### class PolarMapper(AbstractMapper): """ Maps a 1-D data space to and from screen space by specifying a range in data space and a corresponding fixed line in screen space. This class concerns itself only with metric and not with orientation. So, to "flip" the screen space orientation, swap the values for **low_pos** and **high_pos**. """ #------------------------------------------------------------------------ # Private traits #------------------------------------------------------------------------ _scale = Float(1.0) # number of screen space units per data space unit _null_screen_range = Bool(False) _null_data_range = Bool(False) #------------------------------------------------------------------------ # Public methods #------------------------------------------------------------------------ def map_screen(self, data_array): """ map_screen(data_array) -> screen_array Converts radius and theta values from *data_array* to x and y values and then maps values from data space into screen space. """ self._compute_scale() if self._null_data_range: return array([self.low_pos] * len(data_array)) else: return (data_array - self.range.low) * self._scale + self.low_pos def map_data(self, screen_val): """ map_data(screen_val) -> data_val Maps values from screen space into data space. """ self._compute_scale() if self._null_screen_range: return array([self.range.low]) else: return (screen_val - self.low_pos) / self._scale + self.range.low #------------------------------------------------------------------------ # Private methods #------------------------------------------------------------------------ def _compute_scale(self): if self._cache_valid: return if self.range is None: self._cache_valid = False return d = self.range screen_range = self.high_pos - self.low_pos data_range = self._pol_to_rect(d.high) - self._pol_to_rect(d.low) if screen_range == 0.0: self._null_screen_range = True else: self._null_screen_range = False if data_range == 0.0: self._null_data_range = True else: self._scale = screen_range / data_range self._null_data_range = False self._cache_valid = True return # EOF chaco-4.5.0/chaco/polygon_plot.py0000644000076600000240000001430412426462254017542 0ustar jrocherstaff00000000000000""" Defines the PolygonPlot class. """ from __future__ import with_statement # Major library imports import numpy as np # Enthought library imports. from enable.api import LineStyle, black_color_trait, \ transparent_color_trait from kiva.agg import points_in_polygon from traits.api import Enum, Float, Tuple, Property, cached_property, \ on_trait_change # Local imports. from base_xy_plot import BaseXYPlot class PolygonPlot(BaseXYPlot): """ Plots a polygon in dataspace. Assuming that the index and value mappers are linear mappers, and that "index" corresponds to X-coordinates and "value" corresponds to Y-coordinates, the points are arranged in a counter-clockwise fashion. The polygon is closed automatically, so there is no need to reproduce the first point as the last point. Nonlinear mappers are possible, but the results may be unexpected. Only the data-space points are mapped in a nonlinear fashion. Straight lines connecting them in a linear screen-space become curved in a nonlinear screen-space; however, the drawing still contains straight lines in screen-space. If you don't want the edge of the polygon to be drawn, set **edge_color** to transparent; don't try to do this by setting **edge_width** to 0. In some drawing systems, such as PostScript, a line width of 0 means to make the line as small as possible while still putting ink on the page. """ # The color of the line on the edge of the polygon. edge_color = black_color_trait # The thickness of the edge of the polygon. edge_width = Float(1.0) # The line dash style for the edge of the polygon. edge_style = LineStyle # The color of the face of the polygon. face_color = transparent_color_trait # Override the hittest_type trait inherited from BaseXYPlot hittest_type = Enum("poly", "point", "line") # The RGBA tuple for rendering edges. It is always a tuple of length 4. # It has the same RGB values as edge_color_, and its alpha value is the # alpha value of self.edge_color multiplied by self.alpha. effective_edge_color = Property(Tuple, depends_on=['edge_color', 'alpha']) # The RGBA tuple for rendering the face. It is always a tuple of length 4. # It has the same RGB values as face_color_, and its alpha value is the # alpha value of self.face_color multiplied by self.alpha. effective_face_color = Property(Tuple, depends_on=['face_color', 'alpha']) #---------------------------------------------------------------------- # Private 'BaseXYPlot' interface #---------------------------------------------------------------------- def _gather_points(self): """ Collects the data points that are within the bounds of the plot and caches them. """ if self._cache_valid: return index = self.index.get_data() value = self.value.get_data() if not self.index or not self.value: return if len(index) == 0 or len(value) == 0 or len(index) != len(value): self._cached_data_pts = [] self._cache_valid = True return points = np.transpose(np.array((index,value))) self._cached_data_pts = points self._cache_valid = True def _render(self, gc, points): """ Renders an Nx2 array of screen-space points as a polygon. """ with gc: gc.clip_to_rect(self.x, self.y, self.width, self.height) gc.set_stroke_color(self.effective_edge_color) gc.set_line_width(self.edge_width) gc.set_line_dash(self.edge_style_) gc.set_fill_color(self.effective_face_color) gc.lines(points) gc.close_path() gc.draw_path() def _render_icon(self, gc, x, y, width, height): """ Renders a representation of this plot as an icon into the box defined by the parameters. Used by the legend. """ with gc: gc.set_stroke_color(self.effective_edge_color) gc.set_line_width(self.edge_width) gc.set_fill_color(self.effective_face_color) if hasattr(self, 'line_style_'): gc.set_line_dash(self.line_style_) gc.draw_rect((x,y,width,height)) return def hittest(self, screen_pt, threshold=7.0, return_distance=False): """ Performs point-in-polygon testing or point/line proximity testing. If self.hittest_type is "line" or "point", then behaves like the parent class BaseXYPlot.hittest(). If self.hittest_type is "poly", then returns True if the given point is inside the polygon, and False otherwise. """ if self.hittest_type in ("line", "point"): return BaseXYPlot.hittest(self, screen_pt, threshold, return_distance) data_pt = self.map_data(screen_pt, all_values=True) index = self.index.get_data() value = self.value.get_data() poly = np.vstack((index,value)).T if points_in_polygon([data_pt], poly)[0] == 1: return True else: return False #------------------------------------------------------------------------ # Event handlers #------------------------------------------------------------------------ @on_trait_change('edge_color, edge_width, edge_style, face_color, alpha') def _attributes_changed(self): self.invalidate_draw() self.request_redraw() #------------------------------------------------------------------------ # Property getters #------------------------------------------------------------------------ @cached_property def _get_effective_edge_color(self): if len(self.edge_color_) == 4: edge_alpha = self.edge_color_[-1] else: edge_alpha = 1.0 c = self.edge_color_[:3] + (edge_alpha * self.alpha,) return c @cached_property def _get_effective_face_color(self): if len(self.face_color_) == 4: face_alpha = self.face_color_[-1] else: face_alpha = 1.0 c = self.face_color_[:3] + (face_alpha * self.alpha,) return c chaco-4.5.0/chaco/quiverplot.py0000644000076600000240000000675712426452312017236 0ustar jrocherstaff00000000000000 from __future__ import with_statement from numpy import array, compress, matrix, newaxis, sqrt, zeros # Enthought library imports from enable.api import ColorTrait from traits.api import Array, Enum, Float, Instance, Int # Chaco relative imports from abstract_data_source import AbstractDataSource from scatterplot import ScatterPlot class QuiverPlot(ScatterPlot): # Determines how to interpret the data in the **vectors** data source. # "vector": each tuple is a (dx, dy) # "radial": each tuple is an (r, theta) data_type = Enum("vector", "radial") # TODO: implement "radial" # A datasource that returns an Nx2 array array indicating directions # of the vectors. The interpretation of this array is dependent on # the setting of the **data_type** attribute. # # Usually this will be a MultiArrayDataSource. vectors = Instance(AbstractDataSource) #------------------------------------------------------------------------ # Visual attributes of the vector #------------------------------------------------------------------------ # The color of the lines line_color = ColorTrait("black") # The width of the lines line_width = Float(1.0) # The length, in pixels, of the arrowhead arrow_size = Int(5) #------------------------------------------------------------------------ # Private traits #------------------------------------------------------------------------ _cached_vector_data = Array _selected_vector_data = Array def _gather_points_old(self): # In addition to the standard scatterplot _gather_points, we need # to also grab the vectors that fall inside the view range super(QuiverPlot, self)._gather_points_old() if not self.index or not self.value: return if len(self._cached_point_mask) == 0: self._cached_vector_data = [] return vectors = self.vectors.get_data() self._cached_vector_data = compress(self._cached_point_mask, vectors, axis=0) if self._cached_selected_pts is not None: indices = self._cached_selection_point_mask self._selected_vector_data = compress(indices, vectors, axis=0) else: self._selected_vector_data = None return def _render(self, gc, points, icon_mode=False): with gc: gc.clip_to_rect(self.x, self.y, self.width, self.height) gc.set_stroke_color(self.line_color_) gc.set_line_width(self.line_width) # Draw the body of the arrow starts = points ends = points + self._cached_vector_data gc.begin_path() gc.line_set(starts, ends) gc.stroke_path() if self.arrow_size > 0: vec = self._cached_vector_data unit_vec = vec / sqrt(vec[:,0] ** 2 + vec[:,1] ** 2)[:, newaxis] a = 0.707106781 # sqrt(2)/2 # Draw the left arrowhead (for an arrow pointing straight up) arrow_ends = ends - array(unit_vec * matrix([[a, a], [-a, a]])) * self.arrow_size gc.begin_path() gc.line_set(ends, arrow_ends) gc.stroke_path() # Draw the left arrowhead (for an arrow pointing straight up) arrow_ends = ends - array(unit_vec * matrix([[a, -a], [a, a]])) * self.arrow_size gc.begin_path() gc.line_set(ends, arrow_ends) gc.stroke_path() chaco-4.5.0/chaco/scales/0000755000076600000240000000000012426466422015714 5ustar jrocherstaff00000000000000chaco-4.5.0/chaco/scales/__init__.py0000644000076600000240000000027312426452312020020 0ustar jrocherstaff00000000000000""" Generic code to create ticks and labels for data graphics axes. The code in this package does not depend on any specific modules and should be reusable in a variety of contexts. """ chaco-4.5.0/chaco/scales/api.py0000644000076600000240000000010712426452312017026 0ustar jrocherstaff00000000000000from formatters import * from scales import * from time_scale import * chaco-4.5.0/chaco/scales/formatters.py0000644000076600000240000005555212426452312020461 0ustar jrocherstaff00000000000000""" Classes for formatting labels for values or times. """ from math import ceil, floor, fmod, log10 from numpy import abs, all, array, asarray, amax, amin from safetime import strftime, time, safe_fromtimestamp, localtime import warnings __all__ = ['NullFormatter', 'BasicFormatter', 'IntegerFormatter', 'OffsetFormatter', 'TimeFormatter', 'strftimeEx'] class NullFormatter(object): """ Formatter for empty labels. """ def format(ticks, numlabels=None, char_width=None): """ Returns a list containing an empty label for each item in *ticks*. """ return [""] * len(ticks) def estimate_width(start, end, numlabels=None, char_width=None): """ Returns 0 for width and 0 for number of labels. """ return 0, 0 class BasicFormatter(object): """ Formatter for numeric labels. """ # This is a class-level default that is related to the algorithm in format() avg_label_width = 7.0 # Toggles whether or not to use scientific notation when the values exceed # scientific_limits use_scientific = True # Any number smaller than 10 ** limits[0] or larger than 10 ** limits[1] # will be represented using scientific notiation. scientific_limits = (-3, 5) def __init__(self, **kwds): # Allow the user to override the class-level defaults. self.__dict__.update(kwds) def oldformat(self, ticks, numlabels=None, char_width=None): """ This function is adapted from matplotlib's "OldScalarFormatter". Parameters ---------- ticks : array of numbers The tick values to be formatted. numlabels Not used. char_width Not used. Returns ------- List of formatted labels. """ labels = [] if len(ticks) == 0: return [] d = abs(ticks[-1] - ticks[0]) for x in ticks: if abs(x)<1e4 and x==int(x): labels.append('%d' % x) continue if d < 1e-2: fmt = '%1.3e' elif d < 1e-1: fmt = '%1.3f' elif d > 1e5: fmt = '%1.1e' elif d > 10 : fmt = '%1.1f' elif d > 1 : fmt = '%1.2f' else: fmt = '%1.3f' s = fmt % x tup = s.split('e') if len(tup)==2: mantissa = tup[0].rstrip('0').rstrip('.') sign = tup[1][0].replace('+', '') exponent = tup[1][1:].lstrip('0') if sign or exponent: s = '%se%s%s' %(mantissa, sign, exponent) else: s = mantissa else: s = s.rstrip('0').rstrip('.') labels.append(s) return labels def format(self, ticks, numlabels=None, char_width=None, fill_ratio=0.3): """ Does "nice" formatting of floating-point numbers. *numlabels* is ignored in this method. """ if len(ticks) == 0: return [] ticks = asarray(ticks) if self.use_scientific: scientific = (((ticks % 10 ** self.scientific_limits[1]) == 0) | (abs(ticks) <= 10 ** self.scientific_limits[0])).all() else: scientific = False if scientific: if char_width is not None: # We need to determine how many digits we can use in the # mantissa based on the order of magnitude of the exponent. chars_per_label = int(char_width * fill_ratio / len(ticks)) maxtick = amax(abs(ticks)) if maxtick > 0: exp_oom = str(int(floor(log10(maxtick)))) else: exp_oom = "0" emax = len(exp_oom) if chars_per_label < emax: # We're sort of hosed. Use a minimum 3 chars for the mantissa. mmax = 3 else: mmax = chars_per_label - emax - 1 else: mmax = -1 labels = [self._nice_sci(x, mmax) for x in ticks] else: # For decimal mode, if not (ticks % 1).any(): labels = map(str, ticks.astype(int)) else: labels = map(str, ticks) return labels def _nice_sci(self, val, mdigits, force_sign=False): """ Formats *val* nicely using scientific notation. *mdigits* is the max number of digits to use for the mantissa. If *force_sign* is True, then always show the sign of the mantissa, otherwise only show the sign if *val* is negative. """ if val != 0: e = int(floor(log10(abs(val)))) else: e = 0 m = val / float(10**e) m_str = str(m) # Safely truncating the mantissa is somewhat tricky. The minimum # length of the mantissa is everything up to (but not including) the # period. If the m_str doesn't have a decimal point, then we have to # ignore mdigits. if mdigits > 0 and "." in m_str: max_len = max(m_str.index("."), mdigits) m_str = m_str[:max_len] # Strip off a trailing decimal if m_str[-1] == ".": m_str = m_str[:-1] # It's not sufficient just to truncate the string; we need to # handle proper rounding else: # Always strip off a trailing decimal if m_str[-1] == ".": m_str = m_str[:-1] if force_sign and not m_str.startswith("-"): m_str = "+" + m_str if e != 0: # Clean up the exponent e_str = str(e) if e_str.startswith("+") and not force_sign: e_str = e_str[1:] m_str += "e" + e_str return m_str def estimate_width(self, start, end, numlabels=None, char_width=None, fill_ratio=0.3, ticker=None): """ Returns an estimate of the total number of characters used by the the labels for the given set of inputs, as well as the number of labels. Parameters ---------- start : number The beginning of the interval. end : number The end of the interval. numlabels : number The ideal number of labels to generate on the interval. char_width : number The total character width available for labelling the interval. fill_ratio : 0.0 < float <= 1.0 Ratio of the available width that will be occupied by label text. ticker : AbstractScale object Object that can calculate the number of labels needed. Returns ------- (numlabels, total label width) """ if numlabels == 0 or char_width == 0: return 0, 0 # use the start and end points as ticks and average their label sizes labelsizes = map(len, self.format([start, end])) avg_size = sum(labelsizes) / 2.0 if ticker: if numlabels: initial_estimate = numlabels elif char_width: initial_estimate = round(fill_ratio * char_width / avg_size) est_ticks = ticker.num_ticks(start, end, initial_estimate) elif numlabels: est_ticks = numlabels elif char_width: est_ticks = round(fill_ratio * char_width / avg_size) return est_ticks, est_ticks * avg_size class IntegerFormatter(BasicFormatter): """ Format integer tick labels as integers. """ def format(self, ticks, numlabels=None, char_width=None, fill_ratio=0.3): """ Formats integer tick labels. """ return map(str, map(int, ticks)) class OffsetFormatter(BasicFormatter): """ This formatter is like BasicFormatter, but it supports formatting ticks using an offset. This is useful for viewing small ranges within big numbers. """ # Whether or not to use offsets when labelling the ticks. Note that # even if this is true, offset are only used when the ratio of the data # range to the average data value is smaller than a threshold. use_offset = False # The threshold ratio of the data range to the average data value, below # which "offset" display mode will be used if use_offset is True. offset_threshold = 1e-3 # Determines which ticks to display the offset value at. Can be "all", # "firstlast", or "none". offset_display = "firstlast" # Determines which format to use to display the end labels. Can be # "offset" or "sci". end_label_format = "offset" # Specifies the threshold values offset_limits = (-3, 4) # There are two possible formats for the offset. # # "sci" # uses scientific notation for the offset # "decimal" # pads with zeroes left or right until the decimal # # The following table shows some example ranges and how an intermediate # tick will be displayed. These all assume an offset_display value of # "none" or "firstlast". # # ============ ========== ========= ========= # start end sci decimal # ============ ========== ========= ========= # 90.0004 90.0008 5.0e-4 .0005 # 90.0004 90.0015 1.2e-3 .0012 # -1200015 -1200003 12 12 # 2300015000 2300015030 1.502e4 15020 # ============ ========== ========= ========= # offset_format = "sci" # The offset generated by the last call to format() offset = None def _compute_offset(self, ticks): first, last = ticks[0], ticks[-1] data_range = ticks[-1] - ticks[0] range_oom = int(ceil(log10(data_range))) pow_of_ten = 10 ** range_oom if all(asarray(ticks) < 0): return ceil(amax(ticks) / pow_of_ten) * pow_of_ten else: return floor(amin(ticks) / pow_of_ten) * pow_of_ten def format(self, ticks, numlabels=None, char_width=None): if len(ticks) == 0: return [] data_range = ticks[-1] - ticks[0] avg_data = sum(abs(ticks)) / len(ticks) if self.use_offset and data_range/avg_data < self.offset_threshold: offset = self._compute_offset(ticks) intermed_ticks = asarray(ticks) - offset if self.offset_format == "sci": labels = BasicFormatter.format(self, intermed_ticks) else: # have to decide between %d and %f here. also have to # strip trailing "0"s.. test with %g. labels = ["%g" % i for i in intermed_ticks] if offset > 0: sign = "+" else: sign = "" offset_str = BasicFormatter.format(self, [offset])[0] + sign if self.offset_display == "firstlast": if self.end_label_format == "offset": labels[0] = offset_str + labels[0] labels[-1] = offset_str + labels[-1] else: labels[0] = BasicFormatter.format(self, [ticks[0]])[0] labels[-1] = BasicFormatter.format(self, [ticks[-1]])[0] elif self.offset_display == "all": labels = [offset_str + label for label in labels] return labels else: return BasicFormatter.format(self, ticks, numlabels, char_width) def estimate_width(self, start, end, numlabels=None, char_width=None, fill_ratio=0.3, ticker=None): if numlabels == 0 or char_width == 0: return (0, 0) if ticker: if numlabels: initial_estimate = numlabels elif char_width: avg_size = len("%g%g" % (start, end)) / 2.0 initial_estimate = round(fill_ratio * char_width / avg_size) est_ticks = int(ticker.num_ticks(start, end, initial_estimate)) elif numlabels: est_ticks = numlabels elif char_width: est_ticks = round(fill_ratio * char_width / avg_size) start, mid, end = map(len, self.format([start, (start+end)/2.0, end])) if est_ticks > 2: size = start + end + (est_ticks-2) * mid else: size = start + end return est_ticks, size def strftimeEx(fmt, t, timetuple=None): """ Extends time.strftime() to format milliseconds and microseconds. Expects input to be a floating-point number of seconds since epoch. The additional formats are: - ``%(ms)``: milliseconds (uses round()) - ``%(ms_)``: milliseconds (uses floor()) - ``%(us)``: microseconds (uses round()) The format may also be a callable which will bypass time.strftime() entirely. """ if callable(fmt): return fmt(t) if "%(ms)" in fmt: # Assume that fmt does not also contain %(ms_) and %(us). # (It really doesn't make sense to mix %(ms) with those.) secs, frac = divmod(round(t,3), 1) ms = int(round(1e3*frac)) fmt = fmt.replace("%(ms)", "%03d" % ms) else: # Assume fmt contains %(ms_) and %(us). secs, frac = divmod(round(t,6), 1) ms = int(round(1e3*frac)) ms_, us = divmod(int(round(1e6*frac)),1000) fmt = fmt.replace("%(ms_)", "%03d" % ms_) fmt = fmt.replace("%(us)", "%03d" % us) if not timetuple: timetuple = localtime(secs) return strftime(fmt, timetuple) def _two_digit_year(t): """ Round to the nearest Jan 1, roughly. """ dt = safe_fromtimestamp(t) year = dt.year if dt.month >= 7: year += 1 return "'%02d" % (year % 100) def _four_digit_year(t): """ Round to the nearest Jan 1, roughly. """ dt = safe_fromtimestamp(t) year = dt.year if dt.month >= 7: year += 1 return str(year) class TimeFormatter(object): """ Formatter for time values. """ # This table of format is convert into the 'formats' dict. Each tuple of # formats must be ordered from shortest to longest. _formats = { 'microseconds': ('%(us)us', '%(ms_).%(us)ms'), 'milliseconds': ('%(ms)ms', '%S.%(ms)s'), 'seconds': (':%S', '%Ss'), 'minsec': ('%M:%S',), # '%Mm%S', '%Mm%Ss'), 'minutes': ('%Mm',), 'hourmin': ('%H:%M',), #'%Hh%M', '%Hh%Mm', '%H:%M:%S','%Hh %Mm %Ss'), 'hours': ('%Hh', '%H:%M'), 'days': ('%m/%d', '%a%d',), 'months': ('%m/%Y', '%b%y'), 'years': (_two_digit_year, _four_digit_year), } # Labels of time units, from finest to coarsest. format_order = ['microseconds', 'milliseconds', 'seconds', 'minsec', 'minutes', 'hourmin', 'hours', 'days', 'months', 'years'] # A dict whose are keys are the strings in **format_order**; each value is # two arrays, (widths, format strings/functions). formats = {} # Whether or not to strip the leading zeros on tick labels. strip_leading_zeros = True def __init__(self, **kwds): self.__dict__.update(kwds) self._compute_format_weights() def _compute_format_weights(self): if self.formats: return for fmt_name, fmt_strings in self._formats.items(): sizes = [] tmptime = time() for s in fmt_strings: size = len(strftimeEx(s, tmptime)) sizes.append(size) self.formats[fmt_name] = (array(sizes), fmt_strings) return def _get_resolution(self, resolution, interval): r = resolution span = interval if r < 5e-4: resol = "microseconds" elif r < 0.5: resol = "milliseconds" elif r < 60: if span > 60: resol = "minsec" else: resol = "seconds" elif r < 3600: if span > 3600: resol = "hourmin" else: resol = "minutes" elif r < 24*3600: resol = "hours" elif r < 30*24*3600: resol = "days" elif r < 365*24*3600: resol = "months" else: resol = "years" return resol def format(self, ticks, numlabels=None, char_width=None, fill_ratio = 0.3, ticker=None): """ Formats a set of time values. Parameters ---------- ticks : array of numbers The tick values to be formatted numlabels Not used. char_width : number The total character width available for labelling the interval. fill_ratio : 0.0 < float <= 1.0 Ratio of the available width that will be occupied by label text. ticker : AbstractScale object Object that can calculate the number of labels needed. Returns ------- List of formatted labels. """ # In order to pick the right set of labels, we need to determine # the resolution of the ticks. We can do this using a ticker if # it's provided, or by computing the resolution from the actual # ticks we've been given. if len(ticks) == 0: return [] span = abs(ticks[-1] - ticks[0]) if ticker: r = ticker.resolution else: r = span / (len(ticks) - 1) resol = self._get_resolution(r, span) widths, formats = self.formats[resol] format = formats[0] if char_width: # If a width is provided, then we pick the most appropriate scale, # otherwise just use the widest format good_formats = array(formats)[widths * len(ticks) < fill_ratio * char_width] if len(good_formats) > 0: format = good_formats[-1] # Apply the format to the tick values labels = [] resol_ndx = self.format_order.index(resol) # This dictionary maps the name of a time resolution (in self.format_order) # to its index in a time.localtime() timetuple. The default is to map # everything to index 0, which is year. This is not ideal; it might cause # a problem with the tick at midnight, january 1st, 0 a.d. being incorrectly # promoted at certain tick resolutions. time_tuple_ndx_for_resol = dict.fromkeys(self.format_order, 0) time_tuple_ndx_for_resol.update( { "seconds" : 5, "minsec" : 4, "minutes" : 4, "hourmin" : 3, "hours" : 3, }) # As we format each tick, check to see if we are at a boundary of the # next higher unit of time. If so, replace the current format with one # from that resolution. This is not the best heuristic in the world, # but it works! There is some trickiness here due to having to deal # with hybrid formats in a reasonable manner. for t in ticks: try: tm = localtime(t) s = strftimeEx(format, t, tm) except ValueError, e: warnings.warn("Unable to convert tick for timestamp " + str(t)) labels.append("ERR") continue hybrid_handled = False next_ndx = resol_ndx # The way to check that we are at the boundary of the next unit of # time is by checking that we have 0 units of the resolution, i.e. # we are at zero minutes, so display hours, or we are at zero seconds, # so display minutes (and if that is zero as well, then display hours). while tm[ time_tuple_ndx_for_resol[self.format_order[next_ndx]] ] == 0: next_ndx += 1 if next_ndx == len(self.format_order): break if resol in ("minsec", "hourmin") and not hybrid_handled: if (resol == "minsec" and tm.tm_min == 0 and tm.tm_sec != 0) or \ (resol == "hourmin" and tm.tm_hour == 0 and tm.tm_min != 0): next_format = self.formats[self.format_order[resol_ndx-1]][1][0] s = strftimeEx(next_format, t, tm) break else: hybrid_handled = True next_format = self.formats[self.format_order[next_ndx]][1][0] s = strftimeEx(next_format, t, tm) if self.strip_leading_zeros: ss = s.lstrip('0') if ss != s and (ss == '' or not ss[0].isdigit()): # A label such as '000ms' should leave one zero. ss = '0' + ss labels.append(ss) else: labels.append(s) return labels def estimate_width(self, start, end, numlabels=None, char_width=None, fill_ratio = 0.2, ticker=None): """ Returns an estimate of the total number of characters used by the the labels for the given set of inputs, as well as the number of labels. Parameters ---------- start : number The beginning of the interval. end : number The end of the interval. numlabels : number The ideal number of labels to generate on the interval. char_width : number The total character width available for labelling the interval. fill_ratio : 0.0 < float <= 1.0 Ratio of the available width that will be occupied by label text. ticker : AbstractScale object Object that can calculate the number of labels needed. Returns ------- (numlabels, total label width) """ if numlabels == 0 or char_width == 0: return 0, 0 if ticker is None or not hasattr(ticker, "unit"): raise ValueError("TimeFormatter requires a scale.") if not numlabels: numlabels = ticker.num_ticks(start, end) span = abs(end - start) if ticker: r = ticker.resolution else: r = span / numlabels unit = self._get_resolution(r, span) if unit == "milliseconds": return numlabels, numlabels * 6 widths, strings = self.formats[unit] if char_width: # Find an appropriate resolution in self.formats and pick between # the various format strings good_widths = widths[widths * numlabels < fill_ratio * char_width] if len(good_widths) == 0: # All too big, pick the first label width = widths[0] else: # Pick the largest label that fits width = good_widths[-1] width *= numlabels else: # Just pick the middle of the pack of format widths width = widths[ int(len(widths) / 2) ] * numlabels return numlabels, width chaco-4.5.0/chaco/scales/safetime.py0000644000076600000240000000500412426452312020053 0ustar jrocherstaff00000000000000""" This module wraps the standard library time module to gracefully handle bad input values for time. """ import warnings import time as stdlib_time # Yup, we're exposing everything from time. from time import * from datetime import datetime, timedelta, MINYEAR, MAXYEAR __all__ = ([x for x in dir(stdlib_time) if not x.startswith('_')] + ['safe_fromtimestamp', 'datetime', 'timedelta', 'MINYEAR', 'MAXYEAR', 'EPOCH']) EPOCH = datetime.fromtimestamp(0.0) # Can't monkeypatch methods of anything in datetime, so we have to wrap them def safe_fromtimestamp(timestamp, *args, **kwds): """ safe_fromtimestamp(timestamp) -> UTC time from POSIX timestamp. Timestamps outside of the valid range will be assigned datetime objects of Jan 1 of either MINYEAR or MAXYEAR, whichever appears closest. WARNING: This function does not behave properly with Daylight Savings Time, due to a documented issue with datetime arithmetic. """ try: return EPOCH + timedelta(seconds=timestamp) except (ValueError, OverflowError), e: warnings.warn("Timestamp out of range. Returning safe default value.") if timestamp <= 0: return datetime(MINYEAR, 1, 1, 0, 0, 0) else: return datetime(MAXYEAR, 1, 1, 0, 0, 0) def mktime(t): """ mktime(tuple) -> floating point number Convert a time tuple in local time to seconds since the Epoch. Invalid time tuples will be assigned the value 0.0 and a warning will be issued. """ try: return stdlib_time.mktime(t) except (ValueError, OverflowError): warnings.warn("Bad time for mktime(). Returning 0.") # mktime() returns a float return 0.0 def doy(dt): """ Find the day of year of the datetime. The returned DoY is in the range [1-366]. """ date = dt.date() jan01 = date.replace(month=1, day=1) doy = (date - jan01).days + 1 return doy struct_time = type(stdlib_time.localtime()) def localtime(t=None): """ localtime([seconds]) -> (tm_year,tm_mon,tm_day,tm_hour,tm_min,tm_sec,tm_wday,tm_yday,tm_isdst) Convert seconds since the Epoch to a time tuple expressing local time. When 'seconds' is not passed in, convert the current time instead. Modified to accept timestamps before the Epoch. """ if t is None: dt = datetime.now() else: dt = safe_fromtimestamp(t) timetuple = (dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.weekday(), doy(dt), -1) return struct_time(timetuple) chaco-4.5.0/chaco/scales/scales.py0000644000076600000240000004636112426452312017543 0ustar jrocherstaff00000000000000""" Functions and classes that compute ticks and labels for graph axes, with special handling of time and calendar axes. """ from bisect import bisect from math import ceil, floor, log10 from numpy import abs, argmin, array, isnan, linspace # Local imports from formatters import BasicFormatter __all__ = ["AbstractScale", "DefaultScale", "FixedScale", "Pow10Scale", "LogScale", "ScaleSystem", "heckbert_interval", "frange"] def frange(min, max, delta): """ Floating point range. """ count = int(round((max - min) / delta)) + 1 return [min + i*delta for i in range(count)] class AbstractScale(object): """ Defines the general interface for scales. """ DEFAULT_NUM_TICKS = 8 def ticks(self, start, end, desired_ticks=None): """ Returns the set of "nice" positions on this scale that enclose and fall inside the interval (*start*,*end*). Parameters ---------- start : number The beginning of the scale interval. end : number The end of the scale interval. desired_ticks : integer Number of ticks that the caller would like to get """ raise NotImplementedError def num_ticks(self, start, end, desired_ticks=None): """ Returns an approximate number of ticks that this scale produces for the given interval. This method is used by the scale system to determine whether this is the appropriate scale to use for an interval; the returned number of ticks does not have to be exactly the same as what ticks() returns. Parameters ---------- start : number The beginning of the scale interval. end : number The end of the scale interval. desired_ticks : integer Number of ticks that the caller would like to get Returns ------- A float or an integer. """ raise NotImplementedError def labels(self, start, end, numlabels=None, char_width=None): """ Returns a series of ticks and corresponding strings for labels that fall inside the interval (*start*,*end*). Parameters ---------- start : number The beginning of the scale interval. end : number The end of the scale interval. numlabels : number The ideal number of labels to generate on the interval. char_width : number The total character width available for labelling the interval. One of *numlabels* or *char_width* must be provided. If both are provided, then both are considered when picking label density and format. """ ticks = self.ticks(start, end, numlabels) labels = self.formatter.format(ticks, numlabels, char_width) return zip(ticks, labels) def label_width(self, start, end, numlabels=None, char_width=None): """ Returns an estimate of the total number of characters used by the the labels that this scale produces for the given set of inputs, as well as the number of labels. Parameters ---------- start : number The beginning of the scale interval. end : number The end of the scale interval. numlabels : number The ideal number of labels to generate on the interval. char_width : number The total character width available for labelling the interval. Returns ------- (numlabels, total label width) """ return self.formatter.estimate_width(start, end, numlabels, char_width, ticker=self) class FixedScale(AbstractScale): """ A scale with fixed resolution, and "nice" points that line up at multiples of the resolution. An optional zero value can be defined that offsets the "nice" points to (N*resolution+zero). """ def __init__(self, resolution, zero=0.0, formatter=None): self.resolution = resolution self.zero = zero if formatter is None: formatter = BasicFormatter() self.formatter = formatter def ticks(self, start, end, desired_ticks=None): """ For FixedScale, *desired_ticks* is ignored. Overrides AbstractScale. """ if start == end or isnan(start) or isnan(end): return [] res = self.resolution start -= self.zero end -= self.zero start_tick = int(ceil(start / res)) end_tick = int(floor(end / res)) ticks = [i*res for i in range(start_tick, end_tick+1)] return ticks def num_ticks(self, start, end, desired_ticks=None): """ For FixedScale, *desired_ticks* is ignored. Overrides AbstractScale. """ if self.resolution is None or self.resolution == 0.0: return 0 else: return (end - start) / self.resolution def _nice(x, round=False): """ Returns a bracketing interval around interval *x*, whose endpoints fall on "nice" values. If *round* is False, then it uses ceil(range) This function is adapted from the original in Graphics Gems; the boundaries have been changed to use (1, 2.5, 5, 10) as the nice values instead of (1, 2, 5, 10). """ if x <= 0: import warnings warnings.warn("Invalid (negative) range passed to tick interval calculation") x = abs(x) expv = floor(log10(x)) f = x / pow(10, expv) if round: if f < 1.75: nf = 1.0 elif f < 3.75: nf = 2.5 elif f < 7.0: nf = 5.0 else: nf = 10.0 else: if f <= 1.0: nf = 1.0 elif f <= 2.5: nf = 2.5 elif f <= 5.0: nf = 5.0 else: nf = 10.0 return nf * pow(10, expv) def heckbert_interval(data_low, data_high, numticks=8, nicefunc=_nice, enclose=False): """ Returns a "nice" range and resolution for an interval and a preferred number of ticks, using Paul Heckbert's algorithm in Graphics Gems. If *enclose* is True, then the function returns a min and a max that fall inside *data_low* and *data_high*; if *enclose* is False, the nice interval can be larger than the input interval. """ if data_high == data_low: return data_high, data_low, 0 if numticks == 0: numticks = 1 range = nicefunc(data_high - data_low) if numticks > 1: numticks -= 1 d = nicefunc(range / numticks, round=True) if enclose: graphmin = ceil(data_low / d) * d graphmax = floor(data_high / d) * d else: graphmin = floor(data_low / d) * d graphmax = ceil(data_high / d) * d return graphmin, graphmax, d class DefaultScale(AbstractScale): """ A dynamic scale that tries to place ticks at nice numbers (1, 2, 5, 10) so that ticks don't "pop" as the resolution changes. """ def __init__(self, formatter=None): if formatter is None: formatter = BasicFormatter() self.formatter = formatter def ticks(self, start, end, desired_ticks=8): """ Returns the set of "nice" positions on this scale that enclose and fall inside the interval (*start*,*end*). Implements AbstractScale. """ if start == end or isnan(start) or isnan(end): return [start] min, max, delta = heckbert_interval(start, end, desired_ticks, enclose=True) return frange(min, max, delta) def num_ticks(self, start, end, desired_ticks=8): """ Returns an approximate number of ticks that this scale produces for the given interval. Implements AbstractScale. """ return len(self.ticks(start, end, desired_ticks)) class Pow10Scale(AbstractScale): """ A dynamic scale that shows only whole multiples of powers of 10 (including powers < 1). """ def __init__(self, formatter=None): if formatter is None: formatter = BasicFormatter() self.formatter = formatter def ticks(self, start, end, desired_ticks=8): """ Returns the set of "nice" positions on this scale that enclose and fall inside the interval (*start*,*end*). Implements AbstractScale. """ if start == end or isnan(start) or isnan(end): return [start] min, max, delta = heckbert_interval(start, end, desired_ticks, nicefunc=self._nice_pow10, enclose = True) return frange(min, max, delta) def num_ticks(self, start, end, desired_ticks=8): """ Returns an approximate number of ticks that this scale produces for the given interval. Implements AbstractScale. """ return len(self.ticks(start, end, desired_ticks)) def _nice_pow10(self, x, round=False): return pow(10, floor(log10(x))) class LogScale(AbstractScale): """ A dynamic scale that only produces ticks and labels that work well when plotting data on a logarithmic scale. """ def __init__(self, formatter=None): if formatter is None: formatter = BasicFormatter() self.formatter = formatter # In the following utility functions, "irep" stands for "integer representation". # For a given base interval size i (i.e. "magic number"), there is a one-to-one # mapping between the nice tick values and the integers. def _irep_to_value(self,n,i): """ For a given "magic number" i (i.e. spacing of the evenly spaced ticks in the decade [1,10]), compute the tick value of the given integer representation.""" if i == 1: j,k = divmod(n,9) v = (k+1)*10**j return v else: j,k = divmod(n,int(10.0/i)) if k == 0: v = 10**j else: v = i*k*10**j return v def _power_and_interval(self,x,i): # j is the power of 10 of the decade in which x lies j = int(ceil(log10(x))) - 1 # b is the interval size of the evenly spaced ticks in the decade b = i*10**j return (j,b) def _power_and_index_to_irep(self,j,k,i): if i == 1: n = j*9+(k-1) else: n = j*int(10.0/i)+k return n def _logtickceil_as_irep(self,x,i): """ For a given "magic number" i (i.e. spacing of the evenly spaced ticks in the decade [1,10]), compute the integer representation of the smallest tick not less than x.""" j,b = self._power_and_interval(x,i) k = int(ceil(float(x)/b)) n = self._power_and_index_to_irep(j,k,i) return n def _logtickfloor_as_irep(self,x,i): """ For a given "magic number" i (i.e. spacing of the evenly spaced ticks in the decade [1,10]), compute the integer representation of the largest tick not greater than x.""" j,b = self._power_and_interval(x,i) k = int(floor(float(x)/b)) n = self._power_and_index_to_irep(j,k,i) return n def ticks(self, start, end, desired_ticks=8): """ Compute a "nice" set of ticks for a log scale.""" if start > end: start, end = end, start if start == 0.0: # Whoever calls us with a value of 0.0 puts themselves at our mercy log_start = 1e-9 else: log_start = log10(start) if end == 0.0: log_end = 1e-9 else: log_end = log10(end) log_interval = log_end - log_start if log_interval < 1.0: # If the data is spaced by less than a factor of 10, then use # regular/linear ticking min, max, delta = heckbert_interval(start, end, desired_ticks, enclose=True) return frange(min, max, delta) elif log_interval < desired_ticks: magic_numbers = [1, 2, 5] for interval in magic_numbers: n1 = self._logtickceil_as_irep(start,interval) n2 = self._logtickfloor_as_irep(end,interval) ticks = [self._irep_to_value(n,interval) for n in range(n1,n2+1)] if len(ticks) < desired_ticks * 1.5: return ticks return ticks else: # Put lines at every power of ten startlog = ceil(log_start) endlog = floor(log_end) expticks = linspace(startlog, endlog, endlog - startlog + 1) return 10**expticks def num_ticks(self, start, end, desired_ticks=8): """ Returns an approximate number of ticks that this scale produces for the given interval. Implements AbstractScale. """ return len(self.ticks(start, end, desired_ticks)) ############################################################################## # # ScaleSystem # ############################################################################## class ScaleSystem(object): """ Represents a collection of scales over some range of resolutions. This class has settings for a default scale that is used when ticking an interval that is smaller than the finest resolution scale or larger than the coarsest resolution scale. """ def __init__(self, *scales, **kw): """ Creates a ScaleSystem Usage:: ScaleSystem(scale1, .., scaleN, default_scale = DefaultScale()) If *default_scale* is not specified, then an instance of DefaultScale() is created. If no *default_scale* is needed, then set it to None. """ self.scales = scales self.default_scale = kw.get("default_scale", DefaultScale()) # Heuristics for picking labels # The ratio of total label character count to the available character width self.fill_ratio = 0.3 self.default_numticks = 8 def ticks(self, start, end, numticks=None): """ Computes nice locations for tick marks. Parameters ========== start, end : number The start and end values of the data. numticks : number The desired number of ticks to produce. scales : a list of tuples of (min_interval, Scale) Scales to use, in order from fine resolution to coarse. If the end-start interval is less than a particular scale's *min_interval*, then the previous scale is used. Returns ======= A list of positions where the ticks are to be placed. """ if numticks == 0: return [] elif start == end or isnan(start) or isnan(end): return [] elif numticks is None: numticks = self.default_numticks scale = self._get_scale(start, end, numticks) ticks = scale.ticks(start, end, numticks) return ticks def labels(self, start, end, numlabels=None, char_width=None): """ Computes position and labels for an interval Parameters ---------- start : number The beginning of the scale interval. end : number The end of the scale interval. numlabels : number The ideal number of labels to generate on the interval. char_width : number The total character width available for labelling the interval. One of *numlabels* or *char_width* must be provided. If both are provided, then both are considered when picking label density and format. Returns ------- A list of (tick position, string) tuples. """ # Check for insufficient arguments. if numlabels is None and char_width is None: raise ValueError, "Either numlabels or char_width (or both) must be given." if numlabels == 0 or char_width == 0 or isnan(start) or isnan(end): return [] # There are three cases: # 1. we are given numlabels but not char_width # 2. we are given char_width and not numlabels # 3. we are given both # # Case 1: Use numlabels to find the closest scale purely on tick count. # Case 2: Query all scales for their approximate label_width, pick the # closest one to char_width * self.fill_ratio # Case 3: Use numlabels to find the closest scale based on tick count. if numlabels and not char_width: # numlabels was given, but not char_width. scale = self._get_scale(start, end, numlabels) labels = scale.labels(start, end, numlabels) else: # char_width was given. if numlabels: # Both numlabels and char_width were given. scale = self._get_scale(start, end, numlabels) try: ndx = list(self.scales).index(scale) low = max(0, ndx - 1) high = min(len(self.scales), ndx + 1) scales = self.scales[low:high] except ValueError: scales = [scale] else: # Only char_width was given. if len(self.scales) == 0: scales = [self.default_scale] else: scales = self.scales counts, widths = zip(*[s.label_width(start, end, char_width=char_width) \ for s in scales]) widths = array(widths) closest = argmin(abs(widths - char_width*self.fill_ratio)) if numlabels is None: numlabels = scales[closest].num_ticks(start, end, counts[closest]) labels = scales[closest].labels(start, end, numlabels, char_width=char_width) return labels def _get_scale(self, start, end, numticks): if len(self.scales) == 0: closest_scale = self.default_scale else: closest_scale = self._get_scale_np(start, end, numticks) if self.default_scale is not None: # Handle the edge cases and see if there is a major discrepancy between # what the scales offer and the desired number of ticks; if so, revert # to using the default scale approx_ticks = closest_scale.num_ticks(start, end, numticks) if (approx_ticks == 0) or (numticks == 0) or \ (abs(approx_ticks - numticks) / numticks > 1.2) or \ (abs(numticks - approx_ticks) / approx_ticks > 1.2): closest_scale = self.default_scale return closest_scale def _get_scale_bisect(self, start, end, numticks): scale_intervals = [s.num_ticks(start, end, numticks) for s in self.scales] sorted_scales = sorted(zip(scale_intervals, self.scales)) ndx = bisect(sorted_scales, numticks, lo=0, hi=len(self.scales)) if ndx == len(self.scales): ndx -= 1 return sorted_scales[ndx][1] def _get_scale_np(self, start, end, numticks): # Extract the intervals from the scales we were given scale_intervals = array([s.num_ticks(start, end, numticks) for s in self.scales]) closest = argmin(abs(scale_intervals - numticks)) return self.scales[closest] chaco-4.5.0/chaco/scales/scales_test_case.py0000644000076600000240000002145512426452312021572 0ustar jrocherstaff00000000000000 from traits.testing.unittest_tools import unittest from numpy import array from formatters import BasicFormatter, OffsetFormatter from scales import Pow10Scale, FixedScale, LogScale, DefaultScale, ScaleSystem, frange class TicksTestCase(unittest.TestCase): """ Base class for scale and scale system unit tests """ def assert_empty(self, arg): self.assert_(len(arg) == 0) def check_ticks(self, ticks1, ticks2): self.assertEqual(len(ticks1),len(ticks2)) for t1, t2 in zip(ticks1, ticks2): self.assertAlmostEqual(t1, t2, 6) def check_labels(self, labels1, labels2): self.assertEqual(len(labels1),len(labels2)) for t1, t2, in zip(labels1, labels2): self.assert_(t1 == t2) class ScalesTestCase(TicksTestCase): def test_pow10(self): scale = Pow10Scale() ticks = scale.ticks(5,15,8) self.check_ticks(ticks, frange(5, 15, 1.0)) ticks = scale.ticks(5,105,8) self.check_ticks(ticks, frange(10, 100, 10.0)) def test_log_scale_subdecade(self): # Test cases where log_interval is less than 1. scale = LogScale() ticks = scale.ticks(1.0, 2.0) self.check_ticks(ticks, array((1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0))) ticks = scale.ticks(0.9,2.1) self.check_ticks(ticks, array((1.0, 1.25, 1.5, 1.75, 2.0))) ticks = scale.ticks(1.1,9.9) self.check_ticks(ticks, array((2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0))) def test_log_scale_interval1(self): # Test the case where 1 < log_interval < desired_ticks, and interval=1 # is the case that generates the ticks. scale = LogScale() ticks = scale.ticks(1.0,10.1) self.check_ticks(ticks, array((1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0))) ticks = scale.ticks(9.3,99.9) self.check_ticks(ticks, array((10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0))) ticks = scale.ticks(9.9,100.0) self.check_ticks(ticks, array((10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0, 100.0))) def test_log_scale(self): scale = LogScale() ticks = scale.ticks(0.1, 10.0) self.check_ticks(ticks, array((0.1, 0.2, 0.4, 0.6, 0.8, 1.0, 2.0, 4.0, 6.0, 8.0, 10.0))) ticks = scale.ticks(10.0, 1000.0) self.check_ticks(ticks, array((10.0, 20.0, 40.0, 60.0, 80.0, 100.0, 200.0, 400.0, 600.0, 800.0, 1000.0))) ticks = scale.ticks(9.9, 1000.0) self.check_ticks(ticks, array((10.0, 20.0, 40.0, 60.0, 80.0, 100.0, 200.0, 400.0, 600.0, 800.0, 1000.0))) ticks = scale.ticks(5.0, 4300) self.check_ticks(ticks, array((5, 10, 50, 100, 500, 1000))) # Test case when the log_interval is greater than 8 (the # default desired_ticks) ticks = scale.ticks(1e-3,1e6) self.check_ticks(ticks, array((1e-3, 1e-2, 1e-1, 1, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6))) class ScaleSystemTestCase(TicksTestCase): def test_defaults(self): ticker = ScaleSystem() ticks = ticker.ticks(5, 30, 10) self.check_ticks(ticks, frange(5.0, 30.0, 2.5)) def test_fixed_scales(self): scales = [FixedScale(resolution = 1.0), FixedScale(resolution = 10.0), FixedScale(resolution = 100.0)] ticker = ScaleSystem(default_scale=None, *scales) self.check_ticks(ticker.ticks(5, 35, 3), (10.0, 20.0, 30.0)) self.check_ticks(ticker.ticks(5, 35, 20), frange(5.0, 35.0, 1.0)) self.check_ticks(ticker.ticks(5, 614, 10), (100, 200, 300, 400, 500, 600)) def test_revert_to_default(self): scales = [FixedScale(resolution = 1.0), FixedScale(resolution = 10.0), FixedScale(resolution = 100.0)] ticker = ScaleSystem(*scales) ticks = ticker.ticks(2.0, 3.0, 10) self.check_ticks(ticks, frange(2.0, 3.0, 0.1)) def test_translation(self): pass class BasicFormatterTestCase(TicksTestCase): def test_format(self): fmt = BasicFormatter() # test with a fixed scale scale = FixedScale(resolution = 1.0) start, end = 12.0, 18.0 numlabels = 8 ticks = scale.ticks(start, end, numlabels) labels = fmt.format(ticks, numlabels, None) # desired = [str(float(x)) for x in range(12, 19)] ## This test fails when desired is created with str(float(x)). ## The format function returns "12",...,"18", not "12.0",...,"18.0". desired = ["12","13","14","15","16","17","18"] self.check_labels(labels, desired) def test_format_small_numbers(self): fmt = BasicFormatter() numlabels = 8 # test with small numbers scale = FixedScale(resolution = 1e-4) start, end = 5e-5, 8.5e-4 ticks = scale.ticks(start, end, numlabels) labels = fmt.format(ticks, numlabels, None) desired = [str(float(i))+"e-4" for i in range(1, 9)] self.check_labels(labels, desired) def test2_nice_sci(self): # The table of numerical values and their proper representation # given a certain number of mantissa digits vals = [ (3.14159e10, (2, "3e10"), (3, '3.1e10'), (5, '3.141e10')), (123456789, (3, '1.2e8'), (5, '1.234e8')), (-123456, (2, "-1e5"), (3, "-1e5"), (4, "-1.2e5")), (123, (2, "1e2"), (3, "1.2e2"), (4, "1.23e2")), (1.234, (2, "1"), (3, "1.2"), (4, "1.23")), ] fmt = BasicFormatter() for lst in vals: val = lst[0] for mdigits, desired in lst[1:]: s = fmt._nice_sci(val, mdigits) if s != desired: print "Mismatch for", val, "; desired:", desired, "actual:", s def test_estimate_default_scale(self): fmt = BasicFormatter() scale = DefaultScale() # Test using numlabels test_intervals = ((12., 18., 8), (-4., 16., 10), (5e-5, 8.5e-4, 8), (3e8, 6e8, 8), ) for start, end, numlabels in test_intervals: estimate = fmt.estimate_width(start, end, numlabels, ticker=scale)[1] ticks = scale.ticks(start, end, numlabels) labels = fmt.format(ticks, numlabels, None) actual = sum(map(len, labels)) err = abs(estimate - actual) / actual self.assertLess(err, 0.5) return def test_width_based_default_scale(self): scale = ScaleSystem() test_intervals = ((1, 100, 80), (1, 100, 40), (1, 100, 20),) print for start, end, width in test_intervals: labels = scale.labels(start, end, char_width=width) print "(%d,%d)" % (start,end), " avail:", width, print " used:", sum([len(x[1]) for x in labels]) return def test_scale_system(self): scale = ScaleSystem(FixedScale(resolution = 1.0), FixedScale(resolution = 2.5), FixedScale(resolution = 5.0), FixedScale(resolution = 10.0), FixedScale(resolution = 20.0), FixedScale(resolution = 100.0)) test_intervals = ((1,100,200), (1, 100, 80), (1, 100, 40), (1, 100, 20), (1, 100, 5), (1, 10, 100), (1, 10, 50), (1, 10, 20),) print for start, end, width in test_intervals: labels = scale.labels(start, end, char_width=width) print "(%d,%d)" % (start,end), " avail:", width, print " used:", sum([len(x[1]) for x in labels]), print zip(*labels)[1] return class OffsetFormatterTestCase(TicksTestCase): def test_format(self): test_ranges = [(12003, 12015, 1.0), (1.2003, 1.2015, 1e-4), (-1.2015, -1.2003, 1e-4)] for start, end, resol in test_ranges: fmt = OffsetFormatter() fmt.use_offset=True fmt.offset_format = "decimal" fmt.end_label_format = "sci" scale = FixedScale(resolution = resol) numlabels = 12 ticks = scale.ticks(start, end, numlabels) print "range:", start, end labels = fmt.format(ticks, numlabels, None) print "Labels:", labels, "\n" print "estimated width:", fmt.estimate_width(start, end, numlabels) print "actual width:", sum(map(len, labels)) if __name__ == "__main__": import nose nose.run() chaco-4.5.0/chaco/scales/time_scale.py0000644000076600000240000003422312426452312020370 0ustar jrocherstaff00000000000000""" A scale for time and calendar intervals. """ from math import floor from scales import AbstractScale, ScaleSystem, frange, heckbert_interval from formatters import TimeFormatter from safetime import (safe_fromtimestamp, datetime, timedelta, EPOCH, MINYEAR, MAXYEAR) # Labels for date and time units. datetime_scale = ["microsecond", "second", "minute", "hour", "day", "month", "year"] datetime_zeros = zip(datetime_scale, [0, 0, 0, 0, 1, 1, 1]) __all__ = ["TimeScale", "CalendarScaleSystem", "HMSScales", "MDYScales", "trange", "tfrac", "td_to_sec", "dt_to_sec"] def td_to_sec(td): """ Returns the floating point number of seconds in a timedelta object. """ return td.days * 24 * 3600 + td.seconds + td.microseconds * 1e-6 def dt_to_sec(t): """ Returns the floating point number of seconds since the UNIX epoch corresponding to the given datetime instance. This value is more accurate than mktime(t.timetuple()) because it preserves milliseconds. """ return td_to_sec(t - EPOCH) def tfrac(t, **time_unit): """ Performs a calendar-aware split of a time into (aligned_time, frac) over an interval that is a multiple of one of the following time units: "microseconds" "milliseconds", "seconds", "minutes", "hours", "days", "years" Settings of milliseconds..hours are truncated towards 0, days are counted from January 1st of their respective year, and years are counted from 1 AD. This may lead to unexpected rounding if multi-day or multi-year intervals are used. For example: If it is currently 4:15pm on January 3rd, 2007, calling: ``tfrac(time.time(), hours=3)`` returns the UNIX number of seconds corresponding to "January 3rd, 2007 15:00:00" as the aligned time, and the number of seconds in 1 hour and 15 minutes as the fractional part. Parameters ========== t : float time in seconds ``**time_unit`` : dict a single (interval=value) item Returns ======= A tuple: (aligned time as UNIX time, remainder in seconds) """ unit, period = time_unit.items()[0] if unit == "milliseconds": unit = "microsecond" period *= 1000 else: unit = unit[:-1] # strip off the 's' # Find the nearest round date dt = safe_fromtimestamp(t) amt = getattr(dt, unit) ndx = datetime_scale.index(unit) closest_multiple = int(floor(amt / period) * period) if closest_multiple == 0 and unit in ("day", "year"): # TODO: this isn't really quite right for intervals of days > 1... closest_multiple = 1 whole = dt.replace(**{unit: closest_multiple}) whole = whole.replace(**dict(datetime_zeros[:ndx])) frac = td_to_sec(dt - whole) return dt_to_sec(whole), frac def _advance_month(dt, months): """ Advance a datetime object by a given number of months. """ new_month = dt.month + months years, extra_months = divmod(new_month-1, 12) new_month = extra_months + 1 return dt.replace(year=dt.year+years, month=new_month) def trange_months(start, end, months): """ Create a range of timestamps separated by a given number of months. The start of the iteration is always aligned to Jan 1 2000. """ dt_start = safe_fromtimestamp(start) dt_end = safe_fromtimestamp(end) dmonths = (12 * (dt_start.year - 2000) + dt_start.month - 1) % months dt = _advance_month(dt_start.replace(day=1, hour=0, minute=0, second=0, microsecond=0), -dmonths) while dt < dt_start: dt = _advance_month(dt, months) timestamps = [] while dt <= dt_end: timestamps.append(dt_to_sec(dt)) dt = _advance_month(dt, months) return timestamps def _advance_years(dt, years): """ Advance a datetime object by a given number of years. """ return dt.replace(year=dt.year+years) def trange_years(start, end, years): """ Create a range of timestamps separated by a given number of years. The start of the iteration is aligned to Jan 1 2000. """ dt_start = safe_fromtimestamp(start) dt_end = safe_fromtimestamp(end) dyears = (dt_start.year - 2000) % years if dyears < 0: dyears += years dt = datetime(dt_start.year-dyears, 1, 1, 0, 0, 0, 0) while dt < dt_start: dt = _advance_years(dt, years) timestamps = [] while dt <= dt_end: timestamps.append(dt_to_sec(dt)) dt = _advance_years(dt, years) return timestamps def trange(start, end, **time_unit): """ Like range(), but for times, and with "natural" alignment depending on the interval. For example:: t_range(time.time(), time.time()+76*3600, days=2) t_range(start, end, months=3) Parameters ========== start, end : float Time in seconds. *end* must be later than *start*. time_unit : a single (key, int_value) pair The units to use. *key* must be in the list: "milliseconds", "seconds", "minutes", "hours", "days", "months", "years". Months are treated as 30 days, and years are treated as 365 days. Returns ======= A list of times that nicely span the interval, or an empty list if *start* and *end* fall within the same interval. """ if len(time_unit) > 1: raise ValueError("trange() only takes one keyword argument, got %d" % len(time_unit)) # Months and years are non-uniform, so we special-case them. unit, value = time_unit.items()[0] if unit == 'months': return trange_months(start, end, value) elif unit == 'years': return trange_years(start, end, value) # Express start and end ticks as (date, frac) where date is calendar-aligned # with the interval in time_unit. start_whole, start_frac = tfrac(start, **time_unit) end_whole, end_frac = tfrac(end, **time_unit) # Handle some corner-cases if start_whole == end_whole: return [] if start_frac < 1e-6: first_tick_ndx = 0 else: first_tick_ndx = 1 # Convert months and years into days time_unit["days"] = time_unit.setdefault("days", 0) + \ 365 * time_unit.pop("years", 0) + \ 30 * time_unit.pop("months", 0) delta = td_to_sec(timedelta(**time_unit)) count = (end_whole - start_whole) / delta ticks = [start_whole + i*delta for i in range(int(round(count))+1)] return ticks[first_tick_ndx:] class TimeScale(AbstractScale): """ A scale based on time intervals and calendar dates. The valid intervals are: Natural time: microseconds, milliseconds, seconds, minutes, hours, days, years Calendar time: day_of_month, month_of_year For calendar times, a list of hours/days/months is set. By default, intervals are aligned to January 1st. """ # This is used to compute an approximate resolution for each type of scale. SECS_PER_UNIT = {"microseconds": 1e-6, "milliseconds": 1e-3, "seconds": 1, "minutes": 60, "hours": 3600, "days": 24*3600, "day_of_month": 30*24*3600, "month_of_year": 365*24*3600, "years": 365*24*3600, } CALENDAR_UNITS = ("day_of_month", "month_of_year") def __init__(self, **kw_interval): """ Defines the time period that this scale uses. """ self.formatter = kw_interval.pop("formatter", TimeFormatter()) unit, val = kw_interval.items()[0] self.unit = unit if "_of_" in unit: # Calendar time interval - divide by the number of ticks per larger # unit of time to get an average resolution if type(val) in (int, float): val = [val] self.vals = val self.resolution = self.SECS_PER_UNIT[unit] / float(len(val)) else: self.val = val self.resolution = val * self.SECS_PER_UNIT[unit] return def num_ticks(self, start, end, desired_ticks=None): """ Returns an approximate number of ticks that this scale produces for the given interval. Implements AbstractScale. """ # This is only approximate, but puts us in the ballpark if self.unit in ("milliseconds", "microseconds"): ticks = self.ticks(start, end, desired_ticks=8) coarsest_scale_count = (end - start) / (500 * self.SECS_PER_UNIT[self.unit]) return max(len(ticks), coarsest_scale_count) else: return (end - start) / self.resolution def ticks(self, start, end, desired_ticks=None): """ Returns the set of "nice" positions on this scale that enclose and fall inside the interval (*start*,*end*). Implements AbstractScale. The *start* and *end* parameters are floating-point seconds since the epoch. """ if self.unit in self.CALENDAR_UNITS: return self.cal_ticks(start, end) elif self.unit in ("milliseconds", "microseconds"): if start == end or (end - start) < self.SECS_PER_UNIT[self.unit]: return [start] secs_per_unit = self.SECS_PER_UNIT[self.unit] start /= secs_per_unit end /= secs_per_unit if desired_ticks is None: min, max, delta = heckbert_interval(start, end, enclose=True) else: min, max, delta = heckbert_interval(start, end, desired_ticks, enclose=True) min *= secs_per_unit max *= secs_per_unit delta *= secs_per_unit return frange(min, max, delta) else: return trange(start, end, **{self.unit: self.val}) def cal_ticks(self, start, end): """ ticks() method for calendar-based intervals """ try: start = datetime.fromtimestamp(start) except ValueError: start = datetime(MINYEAR, 1, 1, 0, 0, 0) try: end = datetime.fromtimestamp(end) except ValueError: end = datetime(MAXYEAR, 1, 1, 0, 0, 0) if self.unit == "day_of_month": s = start.year + 1/12.0 * start.month e = end.year + 1/12.0 * end.month num_months = int(round((e - s) * 12)) + 1 # add 1 for fencepost start_year = start.year start_month = start.month ym = [divmod(i, 12) for i in range(start_month-1, start_month-1+num_months)] months = [start.replace(year=start_year+y, month=m+1, day=1) for (y,m) in ym] ticks = [dt.replace(day=i) for dt in months for i in self.vals] elif self.unit == "month_of_year": years = [start.replace(year=newyear, day=1) for newyear in range(start.year, end.year+1)] ticks = [dt.replace(month=i, day=1) for dt in years for i in self.vals] else: raise ValueError("Unknown calendar unit '%s'" % self.unit) if len(ticks) > 0: # Find the first and last index in all_ticks that falls # within (start,end) for start_ndx in range(len(ticks)): if ticks[start_ndx] >= start: break for end_ndx in range(len(ticks)-1, 0, -1): if ticks[end_ndx] <= end: break ticks = ticks[start_ndx : end_ndx+1] return map(dt_to_sec, ticks) def labels(self, start, end, numlabels=None, char_width=None): """ Returns a series of ticks and corresponding strings for labels that fall inside the interval (*start*,*end*). Overrides AbstractScale. """ ticks = self.ticks(start, end, numlabels) labels = self.formatter.format(ticks, numlabels, char_width, ticker=self) return zip(ticks, labels) def label_width(self, start, end, numlabels=None, char_width=None): """ Returns an estimate of total number of characters used by the the labels that this scale will produce for the given set of inputs, as well as the number of labels. Overrides AbstractScale. """ return self.formatter.estimate_width(start, end, numlabels, char_width, ticker=self) # Declare some default scale systems # Default time scale for hours, minutes, seconds, and milliseconds. HMSScales = [TimeScale(microseconds=1), TimeScale(milliseconds=1)] + \ [TimeScale(seconds=dt) for dt in (1, 5, 15, 30)] + \ [TimeScale(minutes=dt) for dt in (1, 5, 15, 30)] + \ [TimeScale(hours=dt) for dt in (1, 2, 3, 4, 6, 12, 24)] # Default time scale for months, days, and years. MDYScales = [TimeScale(day_of_month=range(1,31,3)), TimeScale(day_of_month=(1,8,15,22)), TimeScale(day_of_month=(1,15)), TimeScale(month_of_year=range(1,13)), TimeScale(month_of_year=range(1,13,3)), TimeScale(month_of_year=(1,7)), TimeScale(month_of_year=(1,)),] + \ [TimeScale(years=dt) for dt in (1,2,5,10)] class CalendarScaleSystem(ScaleSystem): """ Scale system for calendars. This class has a pre-defined set of nice "time points" to use for ticking and labelling. """ def __init__(self, *scales, **kw): """ Creates a new CalendarScaleSystem. If scales are not provided, then it defaults to HMSScales and MDYScales. """ if len(scales) == 0: scales = HMSScales + MDYScales super(CalendarScaleSystem, self).__init__(*scales, **kw) def _get_scale(self, start, end, numticks): if len(self.scales) == 0: if self.default_scale is not None: closest_scale = self.default_scale else: raise ValueError("CalendarScaleSystem has not be configured " "with any scales.") elif end - start < 1e-6 or end - start > 1e5 * 365 * 24 * 3600: closest_scale = self.default_scale else: closest_scale = self._get_scale_np(start, end, numticks) return closest_scale chaco-4.5.0/chaco/scales/time_scale_test_case.py0000644000076600000240000001664212426452312022427 0ustar jrocherstaff00000000000000 from itertools import starmap from datetime import datetime as DT from scales import ScaleSystem from time_scale import dt_to_sec, trange, TimeScale, HMSScales from formatters import TimeFormatter from scales_test_case import TicksTestCase def DTS(*args, **kw): """ Returns a unix-timestamp-like time """ return dt_to_sec(DT(*args, **kw)) def sec_from_hms(start, *times): """ Returns a list of times based on adding each offset tuple in times to the start time (which should be in seconds). Offset tuples can be in any of the forms: (hours), (hours,minutes), or (hours,minutes,seconds). """ ret = [] for t in times: cur = 0 if len(t) > 0: cur += t[0] * 3600 if len(t) > 1: cur += t[1] * 60 if len(t) > 2: cur += t[2] ret.append(start+cur) return ret class TRangeTestCase(TicksTestCase): def test_null_ranges(self): ranges = ( ((2005,3,15,10,23,15), (2005,3,15,10,23,45), {"minutes":1}), ((2005,3,15,10,23), (2005,3,15,10,47), {"hours":1}), ((2005,3,15,5,23), (2005,3,15,18,43), {"days":1}), ((2005,3,15,10,30), (2005,12,25,18,30), {"years":1}) ) for start, end, kw in ranges: self.assert_empty(trange(DTS(*start), DTS(*end), **kw)) return def test_microseconds(self): # Testing the microsecond scale is dicey--`base` is a 10 digit integer, # so an increment of, say, 3 microseconds is only about a factor of 10 # more than machine precision. base = DTS(2005, 3, 15, 10, 45, 10) print "base: ", base start = base + 0.0000027 end = base + 0.0000177 ticks = trange(start, end, microseconds=5) desired = [base+i for i in (5e-6, 10e-6, 15e-6)] print "ticks: ", ticks print "desired: ", desired self.check_ticks(ticks, desired) def test_milliseconds(self): base = DTS(2005, 3, 15, 10, 45, 10) start = base + 0.0028 end = base + 0.0075 ticks = trange(start, end, milliseconds=1) desired = [base + i for i in (0.003, 0.004, 0.005, 0.006, 0.007)] self.check_ticks(ticks, desired) ticks = trange(start, end, milliseconds=2) self.check_ticks(ticks, (base+0.004, base+0.006)) def test_daily(self): base = DTS(2005, 1, 1) secs_per_day = 24*3600 ticks = trange(base, base + secs_per_day*5, days=1) desired = [base+i*secs_per_day for i in range(6)] print "ticks: ", ticks print "desired: ", desired self.check_ticks(ticks, desired) def test_daily_leap(self): start = DTS(2004, 2, 27) end = DTS(2004, 3, 2) ticks = trange(start, end, days=1) desired = [start + i*24*3600 for i in range(5)] self.check_ticks(ticks, desired) def test_hourly(self): # test between Feb 29,2004 10:15pm and Mar 1st 3:15am ticks = trange(DTS(2004,2,29,22,15), DTS(2004,3,1,3,15), hours=1) start = DTS(2004,2,29,23) desired = [start + i*3600 for i in range(5)] self.check_ticks(ticks, desired) def test_multiday_increment(self): start = DTS(2005, 1, 1) ticks = trange(start, start + 9*24*3600, days=3) desired = [start+i*3*24*3600 for i in range(4)] print "ticks: ", ticks, " desired: ", desired self.check_ticks(ticks, desired) class TimeScaleTestCase(TicksTestCase): """ This exercises a single TimeScale set at various resolutions """ def test_hourly(self): ts = TimeScale(hours=1) start = DTS(2005, 3, 15, 10, 30) end = DTS(2005, 3, 15, 16, 59) desired_start = DTS(2005, 3, 15) desired = [desired_start + i*3600 for i in (11, 12, 13, 14, 15, 16)] self.check_ticks(ts.ticks(start, end), desired) def test_minutes(self): ts = TimeScale(minutes=15) start = DTS(2005, 3, 15, 10, 20) end = DTS(2005, 3, 15, 11, 55) dstart = DTS(2005, 3, 15) desired = ((10,30), (10,45), (11,00), (11,15), (11,30), (11,45)) self.check_ticks(ts.ticks(start, end), sec_from_hms(dstart, *desired)) def test_day_of_month(self): ts = TimeScale(day_of_month=(1,8,15,22)) start = DTS(2005,3,12) end = DTS(2005,5,3) desired = list(starmap(DTS, ((2005,3,15), (2005,3,22), (2005,4,1), (2005,4,8), (2005,4,15), (2005,4,22), (2005,5,1)))) self.check_ticks(ts.ticks(start,end), desired) # test adjacent months start = DTS(2005, 3, 12) end = DTS(2005, 4, 10) desired = list(starmap(DTS, ((2005,3,15), (2005,3,22), (2005,4,1), (2005,4,8)))) self.check_ticks(ts.ticks(start,end), desired) def test_month_of_year(self): ts = TimeScale(month_of_year=(1,4,8)) start = DTS(2005,1,1) end = DTS(2006,5,1) desired = list(starmap(DTS, ((2005,1,1), (2005,4,1), (2005,8,1), (2006,1,1), (2006,4,1)))) self.check_ticks(ts.ticks(start,end), desired) def test_microsecond(self): # This test is dicey, because the values being tested are close to # machine precision. See the comment in TRangeTestCase.test_microseconds(). ts = TimeScale(microseconds=1) base = DTS(1975, 3, 15, 10, 45, 10) start = base + 2.8e-6 end = base + 9.2e-6 ticks = ts.ticks(start, end) desired = [base+i for i in (3e-6, 4e-6, 5e-6, 6e-6, 7e-6, 8e-6, 9e-6)] print "ticks: ", ticks print "desired: ", desired self.check_ticks(ticks, desired) class CalendarScaleSystemTestCase(TicksTestCase): """ This exercises the ability of multiple TimeScale objects to play well within a single ScaleSystem. """ def test_hourly_scales(self): scales = [TimeScale(seconds=dt) for dt in (1, 5, 15, 30)] + \ [TimeScale(minutes=dt) for dt in (1, 5, 15, 30)] + \ [TimeScale(hours=dt) for dt in (1, 2, 3, 4, 6, 12)] def test_yearly_scales(self): ticker = ScaleSystem(TimeScale(month_of_year=1), default_scale=None) ticks = ticker.ticks(DTS(2000,1,1), DTS(2007,1,1), 10) desired = list(starmap(DTS, ((2000,1,1), (2001,1,1), (2002,1,1), (2003,1,1), (2004,1,1), (2005,1,1), (2006,1,1), (2007,1,1)))) self.check_ticks(ticks, desired) class TimeFormatterTestCase(TicksTestCase): def test_widths(self): fmt = TimeFormatter() scale = TimeScale(minutes = 5) test_intervals = ([(2005,3,15,10,30), (2005,3,15,10,50), 50], ) print for start, end, width in test_intervals: est_width = scale.label_width(DTS(*start), DTS(*end), char_width=width) print start, end, print " avail:", width, "est:", est_width[1], "numlabels:", est_width[0] return def test_labels(self): fmt = TimeFormatter() scale = ScaleSystem(*HMSScales) test_intervals = ([(2005,3,15,10,30), (2005,3,15,10,50), 150], ) print for start, end, width in test_intervals: labels = scale.labels(DTS(*start), DTS(*end), char_width=width) print start, end, " avail:", width, print " used:", sum([len(x[1]) for x in labels]), print labels return if __name__ == "__main__": import nose nose.run() chaco-4.5.0/chaco/scales_tick_generator.py0000644000076600000240000000322212426452312021336 0ustar jrocherstaff00000000000000""" Defines the ScalesTickGenerator class. """ from numpy import array from traits.api import Any from enable.font_metrics_provider import font_metrics_provider from ticks import AbstractTickGenerator # Use the new scales/ticks library from scales.api import ScaleSystem class ScalesTickGenerator(AbstractTickGenerator): scale = Any #Instance(ScaleSystem, args=()) font = Any def _scale_default(self): return ScaleSystem() def get_ticks(self, data_low, data_high, bounds_low, bounds_high, interval, use_endpoints=False, scale=None): if interval != "auto": ticks = self.scale.ticks(data_low, data_high, (data_high - data_low) / interval) else: ticks = self.scale.ticks(data_low, data_high) return ticks def get_ticks_and_labels(self, data_low, data_high, bounds_low, bounds_high, orientation = "h"): # TODO: add support for Interval # TODO: add support for vertical labels metrics = font_metrics_provider() if self.font is not None and hasattr(metrics, "set_font"): metrics.set_font(self.font) test_str = "0123456789-+" charsize = metrics.get_full_text_extent(test_str)[0] / len(test_str) numchars = (bounds_high - bounds_low) / charsize tmp = zip(*self.scale.labels(data_low, data_high, numlabels=8, char_width=numchars)) # Check to make sure we actually have labels/ticks to show before # unpacking the return tuple into (tick_array, labels). if len(tmp) == 0: return array([]), [] else: return array(tmp[0]), tmp[1] chaco-4.5.0/chaco/scatter_inspector_overlay.py0000644000076600000240000001331412426452312022303 0ustar jrocherstaff00000000000000 from __future__ import with_statement # Major library imports from numpy import array, asarray # Enthought library imports from enable.api import ColorTrait, MarkerTrait from traits.api import Float, Int, Str, Trait # Local, relative imports from abstract_overlay import AbstractOverlay from scatterplot import render_markers class ScatterInspectorOverlay(AbstractOverlay): """ Highlights points on a scatterplot as the mouse moves over them. Can render the points in a different style, as well as display a DataLabel. Used in conjuction with ScatterInspector. """ # The style to use when a point is hovered over hover_metadata_name = Str('hover') hover_marker = Trait(None, None, MarkerTrait) hover_marker_size = Trait(None, None, Int) hover_line_width = Trait(None, None, Float) hover_color = Trait(None, None, ColorTrait) hover_outline_color = Trait(None, None, ColorTrait) # The style to use when a point has been selected by a click selection_metadata_name = Str('selections') selection_marker = Trait(None, None, MarkerTrait) selection_marker_size = Trait(None, None, Int) selection_line_width = Trait(None, None, Float) selection_color = Trait(None, None, ColorTrait) selection_outline_color = Trait(None, None, ColorTrait) # For now, implement the equivalent of this Traits 3 feature manually # using a series of trait change handlers (defined at the end of the # class) #@on_trait_change('component.index.metadata_changed,component.value.metadata_changed') def metadata_changed(self, object, name, old, new): if self.component is not None: self.component.request_redraw() return def overlay(self, component, gc, view_bounds=None, mode="normal"): plot = self.component if not plot or not plot.index or not getattr(plot, "value", True): return for inspect_type in (self.hover_metadata_name, self.selection_metadata_name): if inspect_type in plot.index.metadata: #if hasattr(plot,"value") and not inspect_type in plot.value.metadata: # continue index = plot.index.metadata.get(inspect_type, None) if index is not None and len(index) > 0: index = asarray(index) index_data = plot.index.get_data() # Only grab the indices which fall within the data range. index = index[index < len(index_data)] # FIXME: In order to work around some problems with the # selection model, we will only use the selection on the # index. The assumption that they are the same is # implicit, though unchecked, already. #value = plot.value.metadata.get(inspect_type, None) value = index if hasattr(plot, "value"): value_data = plot.value.get_data() screen_pts = plot.map_screen(array([index_data[index], value_data[value]]).T) else: screen_pts = plot.map_screen(index_data[index]) if inspect_type == self.selection_metadata_name: prefix = "selection" else: prefix = "hover" self._render_at_indices(gc, screen_pts, prefix) return def _render_at_indices(self, gc, screen_pts, inspect_type): """ screen_pt should always be a list """ self._render_marker_at_indices(gc, screen_pts, inspect_type) def _render_marker_at_indices(self, gc, screen_pts, prefix, sep="_"): """ screen_pt should always be a list """ if len(screen_pts) == 0: return plot = self.component mapped_attribs = ("color", "outline_color", "marker") other_attribs = ("marker_size", "line_width") kwargs = {} for attr in mapped_attribs + other_attribs: if attr in mapped_attribs: # Resolve the mapped trait valname = attr + "_" else: valname = attr tmp = getattr(self, prefix+sep+valname) if tmp is not None: kwargs[attr] = tmp else: kwargs[attr] = getattr(plot, valname) # If the marker type is 'custom', we have to pass in the custom_symbol # kwarg to render_markers. if kwargs.get("marker", None) == "custom": kwargs["custom_symbol"] = plot.custom_symbol with gc: gc.clip_to_rect(plot.x, plot.y, plot.width, plot.height) render_markers(gc, screen_pts, **kwargs) def _draw_overlay(self, gc, view_bounds=None, mode="normal"): self.overlay(self.component, gc, view_bounds, mode) def _component_changed(self, old, new): if old: old.on_trait_change(self._ds_changed, 'index', remove=True) if hasattr(old, "value"): old.on_trait_change(self._ds_changed, 'value', remove=True) if new: for dsname in ("index", "value"): if not hasattr(new, dsname): continue new.on_trait_change(self._ds_changed, dsname) if getattr(new, dsname): self._ds_changed(new, dsname, None, getattr(new,dsname)) return def _ds_changed(self, object, name, old, new): if old: old.on_trait_change(self.metadata_changed, 'metadata_changed', remove=True) if new: new.on_trait_change(self.metadata_changed, 'metadata_changed') return chaco-4.5.0/chaco/scatter_markers.py0000644000076600000240000000125612426452312020202 0ustar jrocherstaff00000000000000# This module used to be the home of the various Marker classes, but they # have since been moved to the enable.markers module. from enable.compiled_path import CompiledPath from enable.markers import AbstractMarker, SquareMarker, \ CircleMarker, TriangleMarker, Inverted_TriangleMarker, PlusMarker, \ CrossMarker, DiamondMarker, DotMarker, PixelMarker, CustomMarker, \ marker_trait, MarkerNameDict, marker_names from kiva.constants import STROKE, FILL_STROKE, \ SQUARE_MARKER, DIAMOND_MARKER, CIRCLE_MARKER, \ CROSS_MARKER, TRIANGLE_MARKER, \ INVERTED_TRIANGLE_MARKER, PLUS_MARKER, DOT_MARKER, \ PIXEL_MARKER, NO_MARKER chaco-4.5.0/chaco/scatterplot.py0000644000076600000240000005110712426462425017363 0ustar jrocherstaff00000000000000""" Defines the ScatterPlot class, and associated Traits UI view and helper function. """ # Standard library imports import itertools # Major library imports from numpy import around, array, asarray, column_stack, \ isfinite, isnan, nanargmin, ndarray, sqrt, sum, transpose, where # Enthought library imports from enable.api import black_color_trait, ColorTrait, AbstractMarker, \ CustomMarker, MarkerNameDict, MarkerTrait from kiva.constants import STROKE from traits.api import Any, Array, Bool, Float, Trait, Callable, Property, \ Tuple, Either, cached_property from traitsui.api import View, VGroup, Item # Local relative imports from base_xy_plot import BaseXYPlot from speedups import scatterplot_gather_points from base import reverse_map_1d #------------------------------------------------------------------------------ # Traits UI View for customizing a scatter plot. #------------------------------------------------------------------------------ class ScatterPlotView(View): """ Traits UI View for customizing a scatter plot. """ def __init__(self): vgroup = VGroup( Item("marker", label="Marker type"), Item("marker_size", label="Size"), Item("color", label="Color", style="custom"), ) super(ScatterPlotView, self).__init__(vgroup) self.buttons = ["OK", "Cancel"] #------------------------------------------------------------------------------ # Helper functions for scatterplot rendering #------------------------------------------------------------------------------ def render_markers(gc, points, marker, marker_size, color, line_width, outline_color, custom_symbol=None, debug=False, point_mask=None): """ Helper function for a PlotComponent instance to render a set of (x,y) points onto a graphics context. Currently, it makes some assumptions about the attributes on the plot object; these may be factored out eventually. Parameters ---------- gc : GraphicsContext The target for rendering the points points : array of (x,y) points The points to render marker : string, class, or instance The type of marker to use for the points marker_size : number The size of the markers color : RGB(A) color The color of the markers line_width : number The width, in pixels, of the marker outline outline_color : RGB(A) color The color of the marker outline custom_symbol : CompiledPath If the marker style is 'custom', this is the symbol point_mask : array of bools The mask specifying which points need to be rendered. The `points` array is already masked """ if len(points) == 0: return # marker can be string, class, or instance if isinstance(marker, basestring): marker = MarkerNameDict[marker]() elif issubclass(marker, AbstractMarker): marker = marker() with gc: gc.set_line_dash(None) if marker.draw_mode == STROKE: # markers with the STROKE draw mode will not be visible # if the line width is zero, so set it to 1 if line_width == 0: line_width = 1.0 gc.set_stroke_color(color) gc.set_line_width(line_width) else: gc.set_stroke_color(outline_color) gc.set_line_width(line_width) gc.set_fill_color(color) gc.begin_path() # try to invoke optimized routines if only one size and gc supports if not isinstance(marker_size, ndarray): # try fastest routine if not isinstance(marker, CustomMarker): # get fast renderer, or dummy if not implemented renderer = getattr(gc, 'draw_marker_at_points', lambda *a: 0) result = renderer(points, marker_size, marker.kiva_marker) # it worked, we're done if result != 0: return # try next fastest routine if hasattr(gc, 'draw_path_at_points'): if not isinstance(marker, CustomMarker): path = gc.get_empty_path() marker.add_to_path(path, marker_size) mode = marker.draw_mode else: path = custom_symbol mode = STROKE if not marker.antialias: gc.set_antialias(False) gc.draw_path_at_points(points, path, mode) return if isinstance(marker_size, ndarray): if point_mask is not None: marker_size = marker_size[point_mask] else: marker_size = itertools.repeat(marker_size) if not marker.antialias: gc.set_antialias(False) if not isinstance(marker, CustomMarker): for pt,size in itertools.izip(points, marker_size): sx, sy = pt with gc: gc.translate_ctm(sx, sy) # Kiva GCs have a path-drawing interface marker.add_to_path(gc, size) gc.draw_path(marker.draw_mode) else: path = custom_symbol for pt,size in itertools.izip(points, marker_size): sx, sy = pt with gc: gc.translate_ctm(sx, sy) gc.scale_ctm(size, size) gc.add_path(path) gc.draw_path(STROKE) return #------------------------------------------------------------------------------ # The scatter plot #------------------------------------------------------------------------------ class ScatterPlot(BaseXYPlot): """ Renders a scatter plot, given an index and value arrays. """ # The CompiledPath to use if **marker** is set to "custom". This attribute # must be a compiled path for the Kiva context onto which this plot will # be rendered. Usually, importing kiva.GraphicsContext will do # the right thing. custom_symbol = Any #------------------------------------------------------------------------ # Styles on a ScatterPlot #------------------------------------------------------------------------ # The type of marker to use. This is a mapped trait using strings as the # keys. marker = MarkerTrait # The pixel size of the markers, not including the thickness of the outline. # Default value is 4.0. # TODO: for consistency, there should be a size data source and a mapper marker_size = Either(Float, Array) # The function which actually renders the markers render_markers_func = Callable(render_markers) # The thickness, in pixels, of the outline to draw around the marker. If # this is 0, no outline is drawn. line_width = Float(1.0) # The fill color of the marker. color = black_color_trait # The color of the outline to draw around the marker. outline_color = black_color_trait # The RGBA tuple for rendering lines. It is always a tuple of length 4. # It has the same RGB values as color_, and its alpha value is the alpha # value of self.color multiplied by self.alpha. effective_color = Property(Tuple, depends_on=['color', 'alpha']) # The RGBA tuple for rendering the fill. It is always a tuple of length 4. # It has the same RGB values as outline_color_, and its alpha value is the # alpha value of self.outline_color multiplied by self.alpha. effective_outline_color = Property(Tuple, depends_on=['outline_color', 'alpha']) # Traits UI View for customizing the plot. traits_view = ScatterPlotView() #------------------------------------------------------------------------ # Selection and selection rendering # A selection on the lot is indicated by setting the index or value # datasource's 'selections' metadata item to a list of indices, or the # 'selection_mask' metadata to a boolean array of the same length as the # datasource. #------------------------------------------------------------------------ show_selection = Bool(True) selection_marker = MarkerTrait selection_marker_size = Float(4.0) selection_line_width = Float(1.0) selection_color = ColorTrait("yellow") selection_outline_color = black_color_trait #------------------------------------------------------------------------ # Private traits #------------------------------------------------------------------------ _cached_selected_pts = Trait(None, None, Array) _cached_selected_screen_pts = Array _cached_point_mask = Array _cached_selection_point_mask = Array _selection_cache_valid = Bool(False) #------------------------------------------------------------------------ # Overridden PlotRenderer methods #------------------------------------------------------------------------ def map_screen(self, data_array): """ Maps an array of data points into screen space and returns it as an array. Implements the AbstractPlotRenderer interface. """ # data_array is Nx2 array if len(data_array) == 0: return [] data_array = asarray(data_array) if len(data_array.shape) == 1: x_ary = data_array[0] y_ary = data_array[1] else: x_ary = data_array[:, 0] y_ary = data_array[:, 1] sx = self.index_mapper.map_screen(x_ary) sy = self.value_mapper.map_screen(y_ary) if self.orientation == "h": return column_stack([sx, sy]) else: return column_stack([sy, sx]) def map_data(self, screen_pt, all_values=True): """ Maps a screen space point into the "index" space of the plot. Overrides the BaseXYPlot implementation, and always returns an array of (index, value) tuples. """ x, y = screen_pt if self.orientation == 'v': x, y = y, x return array((self.index_mapper.map_data(x), self.value_mapper.map_data(y))) def map_index(self, screen_pt, threshold=0.0, outside_returns_none=True, \ index_only = False): """ Maps a screen space point to an index into the plot's index array(s). Overrides the BaseXYPlot implementation.. """ index_data = self.index.get_data() value_data = self.value.get_data() if len(value_data) == 0 or len(index_data) == 0: return None if index_only and self.index.sort_order != "none": data_pt = self.map_data(screen_pt)[0] # The rest of this was copied out of BaseXYPlot. # We can't just used BaseXYPlot.map_index because # it expect map_data to return a value, not a pair. if ((data_pt < self.index_mapper.range.low) or \ (data_pt > self.index_mapper.range.high)) and outside_returns_none: return None try: ndx = reverse_map_1d(index_data, data_pt, self.index.sort_order) except IndexError, e: # if reverse_map raises this exception, it means that data_pt is # outside the range of values in index_data. if outside_returns_none: return None else: if data_pt < index_data[0]: return 0 else: return len(index_data) - 1 if threshold == 0.0: # Don't do any threshold testing return ndx x = index_data[ndx] y = value_data[ndx] if isnan(x) or isnan(y): return None sx, sy = self.map_screen([x,y]) if ((threshold == 0.0) or (screen_pt[0]-sx) < threshold): return ndx else: return None else: # Brute force implementation all_data = transpose(array([index_data, value_data])) screen_points = around(self.map_screen(all_data)) if len(screen_points) == 0: return None if index_only: distances = abs(screen_points[:,0] - screen_pt[0]) else: delta = screen_points - array([screen_pt]) distances = sqrt(sum(delta*delta, axis=1)) closest_ndx = nanargmin(distances) if distances[closest_ndx] <= threshold: return closest_ndx else: return None #------------------------------------------------------------------------ # Private methods; implements the BaseXYPlot stub methods #------------------------------------------------------------------------ def _gather_points_old(self): """ Collects the data points that are within the bounds of the plot and caches them """ if self._cache_valid and self._selection_cache_valid: return if not self.index or not self.value: return index, index_mask = self.index.get_data_mask() value, value_mask = self.value.get_data_mask() if len(index) == 0 or len(value) == 0 or len(index) != len(value): self._cached_data_pts = [] self._cached_point_mask = [] self._cache_valid = True return index_range_mask = self.index_mapper.range.mask_data(index) value_range_mask = self.value_mapper.range.mask_data(value) nan_mask = (isfinite(index) & index_mask & isfinite(value) & value_mask) point_mask = nan_mask & index_range_mask & value_range_mask if not self._cache_valid: if not point_mask.all(): points = column_stack([index[point_mask], value[point_mask]]) else: points = column_stack([index, value]) self._cached_data_pts = points self._cached_point_mask = point_mask self._cache_valid = True if not self._selection_cache_valid: indices = None # Check both datasources for metadata # XXX: Only one is used, and if both are defined, then self.index # happens to take precendence. Perhaps this should be more # structured? Hopefully, when we create the Selection objects, # we'll have to define a small algebra about how they are combined, # and this will fall out... point_mask = point_mask.copy() for ds in (self.index, self.value): if ds.metadata.get('selection_masks', None) is not None: try: for mask in ds.metadata['selection_masks']: point_mask &= mask indices = where(point_mask == True) points = column_stack([index[indices], value[indices]]) except: continue elif ds.metadata.get('selections', None) is not None: try: indices = ds.metadata['selections'] point_mask = point_mask[indices] points = column_stack([index[indices], value[indices]]) except: continue else: continue self._cached_selection_point_mask = point_mask self._cached_selected_pts = points self._selection_cache_valid = True break else: self._cached_selected_pts = None self._selection_cache_valid = True return def _gather_points_fast(self): if self._cache_valid and self._selection_cache_valid: return if not self.index or not self.value: return index, index_mask = self.index.get_data_mask() value, value_mask = self.value.get_data_mask() index_range = self.index_mapper.range value_range = self.value_mapper.range kw = {} for axis in ("index", "value"): ds = getattr(self, axis) if ds.metadata.get('selections', None) is not None: kw[axis + "_sel"] = ds.metadata['selections'] if ds.metadata.get('selection_mask', None) is not None: kw[axis + "_sel_mask"] = ds.metadata['selection_mask'] points, selections = scatterplot_gather_points(index, index_range.low, index_range.high, value, value_range.low, value_range.high, index_mask = index_mask, value_mask = value_mask, **kw) if not self._cache_valid: self._cached_data_pts = points self._cache_valid = True if not self._selection_cache_valid: if selections is not None and len(selections) > 0: self._cached_selected_pts = points[selections] self._selection_cache_valid = True else: self._cached_selected_pts = None self._selection_cache_valid = True def _gather_points(self): #self._gather_points_fast() self._gather_points_old() def _render(self, gc, points, icon_mode=False): """ This same method is used both to render the scatterplot and to draw just the iconified version of this plot, with the latter simply requiring that a few steps be skipped. """ if not icon_mode: gc.save_state() gc.clip_to_rect(self.x, self.y, self.width, self.height) self.render_markers_func(gc, points, self.marker, self.marker_size, self.effective_color, self.line_width, self.effective_outline_color, self.custom_symbol, point_mask=self._cached_point_mask) if self._cached_selected_pts is not None and len(self._cached_selected_pts) > 0: sel_pts = self.map_screen(self._cached_selected_pts) self.render_markers_func(gc, sel_pts, self.selection_marker, self.selection_marker_size, self.selection_color_, self.selection_line_width, self.selection_outline_color_, self.custom_symbol, point_mask=self._cached_point_mask) if not icon_mode: # Draw the default axes, if necessary self._draw_default_axes(gc) gc.restore_state() def _render_icon(self, gc, x, y, width, height): point = array([x+width/2, y+height/2]) self._render(gc, [point], icon_mode=True) return #------------------------------------------------------------------------ # Event handlers #------------------------------------------------------------------------ def _alpha_changed(self): self.invalidate_draw() self.request_redraw() def _marker_changed(self): self.invalidate_draw() self.request_redraw() def _marker_size_changed(self): self.invalidate_draw() self.request_redraw() def _line_width_changed(self): self.invalidate_draw() self.request_redraw() def _color_changed(self): self.invalidate_draw() self.request_redraw() def _outline_color_changed(self): self.invalidate_draw() self.request_redraw() def _either_metadata_changed(self): if self.show_selection: # Only redraw when we are showing the selection. Otherwise, there # is nothing to update in response to this event. self._selection_cache_valid = False self.invalidate_draw() self.request_redraw() #------------------------------------------------------------------------ # Defaults #------------------------------------------------------------------------ def _marker_size_default(self): return 4.0 #------------------------------------------------------------------------ # Properties #------------------------------------------------------------------------ @cached_property def _get_effective_color(self): if len(self.color_) == 4: edge_alpha = self.color_[-1] else: edge_alpha = 1.0 c = self.color_[:3] + (edge_alpha * self.alpha,) return c @cached_property def _get_effective_outline_color(self): if len(self.outline_color_) == 4: edge_alpha = self.outline_color_[-1] else: edge_alpha = 1.0 c = self.outline_color_[:3] + (edge_alpha * self.alpha,) return c # EOF chaco-4.5.0/chaco/selectable_legend.py0000644000076600000240000000477012426452312020436 0ustar jrocherstaff00000000000000 from chaco.tools.api import SelectTool from traits.api import List from legend import Legend class SelectableLegend(Legend, SelectTool): # A list of indices into self._cached_labels that indicates which labels # should be rendered in the "selected" style selections = List # A cached list of tuples (x,y,w,h) of each label's geometry _cached_label_dims = List #------------------------------------------------------------------------ # Legend methods #------------------------------------------------------------------------ def _do_layout(self): Legend._do_layout(self) self._compute_label_dims() def _compute_label_dims(self): dims = [] edge_space = self.border_width + self.border_padding icon_width, icon_height = self.icon_bounds icon_x = self.x + edge_space text_x = icon_x + icon_width + self.icon_spacing y = self.y2 - edge_space for i, label_name in enumerate(self._cached_label_names): label_width, label_height = self._cached_label_sizes[i] y -= label_height icon_y = y + (label_height - icon_height) / 2 dims.append((icon_x, icon_y, icon_width + self.icon_spacing + label_width, label_height)) y -= self.line_spacing self._cached_label_dims = dims #------------------------------------------------------------------------ # SelectTool interface #------------------------------------------------------------------------ def _get_selection_state(self, event): for ndx, dims in enumerate(self._cached_label_dims): x, y, w, h = dims if (x <= event.x <= x+w) and (y <= event.y <= y+h): return (ndx in self.selections), True else: if len(self._cached_label_dims) > 0: return (ndx in self.selections), False else: return False, False def _get_selection_token(self, event): for ndx, dims in enumerate(self._cached_label_dims): x, y, w, h = dims if (x <= event.x <= x+w) and (y <= event.y <= y+h): return ndx else: return None def _select(self, index, append=True): if append: self.selections.append(index) else: self.selections = [index] return def _deselect(self, index=None): if index in self.selections: self.selections.remove(index) return chaco-4.5.0/chaco/selectable_overlay_container.py0000644000076600000240000000521012426452312022711 0ustar jrocherstaff00000000000000""" Defines the SelectableOverlayPlotContainer class. """ from __future__ import with_statement from numpy import array, float64 # Enthought library imports from traits.api import Bool, Float, Enum from enable.api import ColorTrait # Local imports from plot_containers import OverlayPlotContainer class SelectableOverlayPlotContainer(OverlayPlotContainer): """ An OverlayPlotContainer that can show a selection region on top of it. """ # Screen position of the start of the selection, which can be in the x- or # y-dimension, depending on **selection_direction**. selection_screen_start = Float(0.0) # Screen position of the end of the selection, which can be in the x- or # y-dimension, depending on **selection_direction**. selection_screen_end = Float(0.0) # Is there an active selection? selection_active = Bool(False) # The direction of the selection. selection_direction = Enum('v', 'h') # The color to use to fill the selected region. selection_fill_color = ColorTrait('lightskyblue') # The color to use to draw the border of the selected region. selection_border_color = ColorTrait('dodgerblue') # The transparency of the **selection_fill_color**. selection_alpha = Float(0.3) def _draw_overlays(self, gc, view_bounds=None, mode='normal'): """ Method for backward compatability with old drawing scheme. Overrides BasePlotContainer. """ self._draw_selection(gc, view_bounds=view_bounds, mode=mode) return def _draw_selection(self, gc, view_bounds=None, mode='normal'): """ Renders a selected subset of a component's data. Overrides PlotComponent. """ if self.selection_active: if self.selection_direction == 'h': x1 = self.selection_screen_start x2 = self.selection_screen_end y1 = self.y y2 = self.position[1] + self.bounds[1] - 1 else: x1 = self.x x2 = self.position[0] + self.bounds[0] - 1 y1 = self.selection_screen_start y2 = self.selection_screen_end lowerleft = array((min(x1, x2), min(y1, y2)), float64) upperright = array((max(x1, x2), max(y1, y2)), float64) with gc: gc.translate_ctm(*self.position) gc.set_fill_color(self.selection_fill_color_) gc.set_stroke_color(self.selection_border_color_) gc.set_alpha(self.selection_alpha) gc.rect(lowerleft[0], lowerleft[1], upperright[0], upperright[1]) gc.draw_path() return chaco-4.5.0/chaco/serializable.py0000644000076600000240000000760412426452312017462 0ustar jrocherstaff00000000000000""" Defines the Serializable mix-in class. """ class Serializable(object): """ Mix-in class to help serialization. Serializes just the attributes in **_pickles**. This mix-in works best when all the classes in a hierarchy subclass from it. It solves the problem of allowing each class to specify its own set of attributes to pickle and attributes to ignore, without having to also implement __getstate__ and __setstate__. """ # The basic list of attributes to save. These get set without firing # any trait events. _pickles = None # A list of the parents of this class that will be searched for their # list of _pickles. Only the parents in this list that inherit from # Serialized will be pickled. The process stops at the first item in # __pickle_parents that is not a subclass of Serialized. # # This is a double-underscore variable so that Python's attribute name # will shield base class # __pickle_parents = None def _get_pickle_parents(self): """ Subclasses can override this method to return the list of base classes they want to have the serializer look at. """ bases = [] for cls in self.__class__.__mro__: if cls is Serializable: # don't add Serializable to the list of parents continue elif issubclass(cls, Serializable): bases.append(cls) else: break return bases def _pre_save(self): """ Called before __getstate__ to give the object a chance to tidy up and get ready to be saved. This usually also calls the superclass. """ return def _post_load(self): """ Called after __setstate__ finishes restoring the state on the object. This method usually needs to include a call to super(cls, self)._post_load(). Avoid explicitly calling a parent class by name, because in general you want post_load() to happen in the same order as MRO, which super() does automatically. """ print 'Serializable._post_load' pass def _do_setstate(self, state): """ Called by __setstate__ to allow the subclass to set its state in a special way. Subclasses should override this instead of Serializable.__setstate__ because we need Serializable's implementation to call _post_load() after all the _do_setstate() have returned.) """ # Quietly all the attributes self.set(trait_change_notify=False, **state) return #------------------------------------------------------------------------ # Private methods #------------------------------------------------------------------------ # def __getstate__(self): # #idstring = self.__class__.__name__ + " id=" + str(id(self)) # # Give the object a chance to tidy up before saving # self._pre_save() # # # Get the attributes that this class needs to serialize. We do this by # # marching up the list of parent classes in _pickle_parents and getting # # their lists of _pickles. # all_pickles = Set() # pickle_parents = self._get_pickle_parents() # for parent_class in pickle_parents: # all_pickles.update(parent_class._pickles) # # if self._pickles is not None: # all_pickles.update(self._pickles) # # state = {} # for attrib in all_pickles: # state[attrib] = getattr(self, attrib) # # print '<<<<<<<<<<<<<', self # for key,value in state.items(): # print key, type(value) # print '>>>>>>>>>>>>>' # # return state #~ def __setstate__(self, state): #~ idstring = self.__class__.__name__ + " id=" + str(id(self)) #~ self._do_setstate(state) #~ self._post_load() #~ return # EOF chaco-4.5.0/chaco/shell/0000755000076600000240000000000012426466422015551 5ustar jrocherstaff00000000000000chaco-4.5.0/chaco/shell/__init__.py0000644000076600000240000000003012426452312017644 0ustar jrocherstaff00000000000000 from commands import * chaco-4.5.0/chaco/shell/chaco_shell_error.py0000644000076600000240000000022212426452312021565 0ustar jrocherstaff00000000000000""" Defines the ChacoShellError class. """ class ChacoShellError(RuntimeError): """ Error raised by the Chaco shell. """ pass # EOF chaco-4.5.0/chaco/shell/commands.py0000644000076600000240000005675712426452312017741 0ustar jrocherstaff00000000000000""" Defines commands for the Chaco shell. """ try: from wx import GetApp except ImportError: GetApp = lambda: None from chaco.api import Plot, color_map_name_dict from chaco.scales.api import ScaleSystem from chaco.tools.api import PanTool, ZoomTool # Note: these are imported to be exposed in the namespace. from chaco.scales.api import (FixedScale, Pow10Scale, LogScale, CalendarScaleSystem) from chaco.default_colormaps import * import plot_maker from session import PlotSession session = PlotSession() #------------------------------------------------------------------------ # General help commands #------------------------------------------------------------------------ def chaco_commands(): """ Prints the current list of all shell commands. Information on each command is available in that command's docstring (__doc__). Window/Plot Management ---------------------- figure creates a new figure window activate activates an existing window or plot close closes a window curplot returns a reference to the active window's Plot object show starts the GUI and displays windows (should only be used in scripts) Plotting -------- plot plots some data imread creates an array from an image file on disk imshow creates an image plot from a file on disk pcolor plots some scalar data as a pseudocolor image contour creates a contour line plot of some scalar data contourf creates a contour poly plot of some scalar data loglog plots an x-y line or scatter plot on log-log scale semilogx plots an x-y line or scatter plot with a log x-scale semilogy plots an x-y line or scatter plot with a log y-scale hold turns "hold" on or off show shows plot on screen; used when running from script Axes, Annotations, Legends -------------------------- xaxis toggles the horizontal axis, sets the interval yaxis toggles the vertical axis, sets the interval xgrid toggles the grid running along the X axis ygrid toggles the grid running along the Y axis xtitle sets the title of a horizontal axis ytitle sets the title of a vertical axis xscale sets the tick scale system of the X axis yscale sets the tick scale system of the Y axis title sets the title of the plot Tools ----- colormap sets the current colormap IO -- save saves the current plot to a file (png, bmp, jpg, pdf) """ print chaco_commands.__doc__ # The following are not implemented yet """ tool -- toggles certain tools on or off load -- loads a saved plot from file into the active plot area scatter -- plots some data as a scatterplot (unordered X/Y data) line -- plots some data as an ordered set of of X,Y points label -- adds a label at a data point legend -- creates a legend and adds it to the plot Layout ------ names -- temporarily overlays plot areas with their names hidenames -- force remove the name overlays from show_names happend -- create a new plot area horizontally after the active plot vappend -- create a new plot area vertically after the active plot hsplit -- splits the current plot into two horizontal subplots vsplit -- splits the current plot into two vertical subplots save_layout -- saves the current layout of plots and plots areas load_layout -- loads a saved layout of plot areas and applies it to the current set of plots Sessions -------- save_session -- saves the current "workspace", defined as the set of active windows and plots load_session -- restores a previously-saved session save_prefs -- saves the current session's preferences, either in a separate file or as the chaco.shell defaults load_prefs -- loads a previously-saved set of preferences """ #------------------------------------------------------------------------ # Window management commands #------------------------------------------------------------------------ def figure(name=None, title=None): """ Creates a new figure window and returns its index. Parameters ---------- name : string The name to use for this window. If this parameter is provided, then this name can be used instead of the window's integer index in other window-related functions. title : string The title of the plot window. If this is blank but *name* is provided, then that is used. If neither *name* nor *title* is provided, then the method uses the value of default_window_name in the Preferences. """ win = session.new_window(name, title) activate(win) return win def activate(ident=None): """ Activates and raises a figure window. Parameters ---------- ident : integer or string Index or name of the window. If neither is specified, then the function raises the currently active window. """ if ident is not None: win = session.get_window(ident) else: win = session.active_window if win is not None: session.active_window = win win.raise_window() return def show(): """ Shows all the figure windows that have been created thus far, and creates a GUI main loop. This function is useful in scripts to show plots and keep their windows open, and has no effect when used from the interpreter prompt. """ from traits.etsconfig.api import ETSConfig from pyface.util import guisupport is_event_loop_running = getattr(guisupport, 'is_event_loop_running_' + ETSConfig.toolkit) start_event_loop = getattr(guisupport, 'start_event_loop_' + ETSConfig.toolkit) if not is_event_loop_running(): frame = session.active_window frame.raise_window() start_event_loop() return def close(ident=None): """ Closes a figure window Parameters ---------- ident : integer or string Index or name of the window to close, or "all". If nothing is specified, then the function closes the active window. """ win_list = [] if ident is None: win_list.append(session.active_window) elif ident == 'all': win_list = session.windows else: win_list.append(session.get_window(ident)) for win in win_list: win.close() return def colormap(map): """Sets the active colormap. Parameters ---------- map : a string, or a callable The color map to use; if it is a string, it is the name of a default colormap; if it is a callable, it must return an AbstractColorMap. """ if isinstance(map, basestring): session.colormap = color_map_name_dict[map] else: session.colormap = map def hold(state=None): """ Turns "hold" on or off, or toggles the current state if none is given. Parameters ---------- state : Boolean The desired hold state. """ if state is None: session.hold = not session.hold else: session.hold = state return def curplot(): if session.active_window: return session.active_window.get_container() else: return None #------------------------------------------------------------------------ # Plotting functions #------------------------------------------------------------------------ def _do_plot_boilerplate(kwargs, image=False): """ Used by various plotting functions. Checks/handles hold state, returns a Plot object for the plotting function to use. """ if kwargs.has_key("hold"): hold(kwargs["hold"]) del kwargs["hold"] # Check for an active window; if none, open one. if len(session.windows) == 0: if image: win = session.new_window(is_image=True) activate(win) else: figure() cont = session.active_window.get_container() if not cont: cont = Plot(session.data) session.active_window.set_container(cont) existing_tools = [type(t) for t in (cont.tools + cont.overlays)] if not PanTool in existing_tools: cont.tools.append(PanTool(cont)) if not ZoomTool in existing_tools: cont.overlays.append(ZoomTool(cont, tool_mode="box", always_on=True, drag_button="right")) if not session.hold: cont.delplot(*cont.plots.keys()) return cont def plot(*data, **kwargs): """ Plots data in a Matlab-compatible way. Data is assumed to be X vs Y. Any additional *kwargs* passed in are broadcast to all plots. Example:: x = arange(-pi, pi, pi/100.) plot(x, sin(x), "b-") To use previous data, specify names instead of actual data arrays. """ cont = _do_plot_boilerplate(kwargs) plots = plot_maker.do_plot(session.data, cont, *data, **kwargs) cont.request_redraw() return def semilogx(*data, **kwargs): """ Plots data on a semilog scale in a Matlab-compatible way. Data is assumed to be X vs Y. Any additional *kwargs* passed in are broadcast to all plots. Example:: x = linspace(0.01, 10.0 100) semilogx(x, sqrt(x), "b-") To use previous data, specify names instead of actual data arrays. Adding a semilog plot to an active plot with a currently different scale rescales the plot. """ kwargs["index_scale"] = "log" plot(*data, **kwargs) def semilogy(*data, **kwargs): """ Plots data on a semilog scale in a Matlab-compatible way. Data is assumed to be X vs Y. Any additional *kwargs* passed in are broadcast to all plots. Example:: x = linspace(0, 10.0, 100) semilogy(x, exp(x), "b-") To use previous data, specify names instead of actual data arrays. Adding a semilog plot to an active plot with a currently different scale rescales the plot. """ kwargs["value_scale"] = "log" plot(*data, **kwargs) def loglog(*data, **kwargs): """ Plots data on a log-log scale in a Matlab-compatible way. Data is assumed to be X vs Y. Any additional *kwargs* passed in are broadcast to all plots. Example:: x = linspace(0.001, 10.0, 100) loglog(x, x**2, "b-") To use previous data, specify names instead of actual data arrays. Adding a log-log plot to an active plot with a currently different scale rescales the plot. """ kwargs["index_scale"] = "log" kwargs["value_scale"] = "log" plot(*data, **kwargs) def imread(*data, **kwargs): """ Returns image file as an array. """ return plot_maker.do_imread(*data, **kwargs) def imshow(*data, **kwargs): """ Creates an image plot from a file on disk. Takes either filename or image data. Any additional *kwargs* passed in are broadcast to all plots. Example 1:: imshow("example.jpg") Example 2:: image = ImageData.fromfile("example.jpg") imshow(image) To use previous data, specify names instead of filename or data arrays. """ cont = _do_plot_boilerplate(kwargs, image=True) if "colormap" not in kwargs: kwargs["colormap"] = session.colormap plots = plot_maker.do_imshow(session.data, cont, *data, **kwargs) cont.request_redraw() return def pcolor(*data, **kwargs): """ Colormaps scalar data in a roughly Matlab-compatible way. Data are assumed to be a scalar image. Any additional *kwargs* passed in are broadcast to all plots. Example:: xs = linspace(0,10,100) ys = linspace(0,20,200) x,y=meshgrid(xs,ys) z = sin(x)*y pcolor(x, y, z) To use previous data, specify names instead of actual data arrays. """ cont = _do_plot_boilerplate(kwargs) plots = plot_maker.do_pcolor(session.data, session.colormap, cont, *data, **kwargs) cont.request_redraw() return def contour(*data, **kwargs): """ Contour line plots of scalar data in a roughly Matlab-compatible way. Data are assumed to be a scalar image. Any additional *kwargs* passed in are broadcast to all plots. Example:: xs = linspace(0,10,100) ys = linspace(0,20,200) x,y=meshgrid(xs,ys) z = sin(x)*y contour(z) To use previous data, specify names instead of actual data arrays. """ cont = _do_plot_boilerplate(kwargs) plots = plot_maker.do_contour(session.data, session.colormap, cont, "line", *data, **kwargs) cont.request_redraw() return def contourf(*data, **kwargs): """ Contour polygon plots of scalar data in a roughly Matlab-compatible way. Data are assumed to be a scalar image. Any additional *kwargs* passed in are broadcast to all plots. Example:: xs = linspace(0,10,100) ys = linspace(0,20,200) x,y=meshgrid(xs,ys) z = sin(x)*y contourf(z) To use previous data, specify names instead of actual data arrays. """ cont = _do_plot_boilerplate(kwargs) plots = plot_maker.do_contour(session.data, session.colormap, cont, "poly", *data, **kwargs) cont.request_redraw() return def plotv(*args, **kwargs): """ Creates a plot of a particular type, or using a "best guess" approach based on the data, using chaco semantics. The number and shape of data arrays determine how the data is interpreted, and how many plots are created. Single-dimensional arrays (shape = (N,)) ---------------------------------------- 1. Single array: the data is treated as the value array, and an index array is generated automatically using arange(len(value)) 2. Multiple arrays: the first array is treated as the index array, and each subsequent array is used as the value for a new plot. All of the plots share a common index (first array). Multi-dimensional arrays (shape = (N,2) or (2,N)) ------------------------------------------------- 1. Single array (NxM or MxN, N > M): interpreted as M-1 plots of N data points each, just like in the multiple 1D array case above. 2. Multiple arrays: each array is treated as a separate set of inter- related plots, with its own index and value data sources Keyword Arguments ----------------- type comma-separated combination of "line", "scatter", "polar" sort "ascending", "descending", or "none", indicating the sorting order of the array that will be used as the index color the color of the plot line and/or marker bgcolor the background color of the plot grid boolean specifying whether or not to draw a grid on the plot axis boolean specifying whether or not to draw an axis on the plot orientation "h" for index on the X axis, "v" for index on the Y axis Scatter plot keywords --------------------- marker the type of marker to use (square, diamond, circle, cross, crossed circle, triangle, inverted triangle, plus, dot, pixel marker_size the size (in pixels) of the marker outline_color the color of the marker outline Line plot keywords ------------------ width the thickness of the line dash the dash style to use (solid, dot dash, dash, dot, long dash) """ cont = _do_plot_boilerplate(kwargs) plots = plot_maker.do_plotv(session, *args, **kwargs) cont.add(*plots) cont.request_redraw() return #----------------------------------------------------------------------------- # Annotations #----------------------------------------------------------------------------- def xtitle(text): """ Sets the horizontal axis label to *text*. """ p = curplot() if p: p.x_axis.title = text p.request_redraw() def ytitle(text): """ Sets the vertical axis label to *text*. """ p = curplot() if p: p.y_axis.title = text p.request_redraw() def title(text): """ Sets the plot title to *text*. """ p = curplot() if p: p.title = text p.request_redraw() _axis_params = """Parameters ---------- title : str The text of the title title_font : KivaFont('modern 12') The font in which to render the title title_color : color ('color_name' or (red, green, blue, [alpha]) tuple) The color in which to render the title tick_weight : float The thickness (in pixels) of each tick tick_color : color ('color_name' or (red, green, blue, [alpha]) tuple) The color of the ticks tick_label_font : KivaFont('modern 10') The font in which to render the tick labels tick_label_color : color ('color_name' or (red, green, blue, [alpha]) tuple) The color of the tick labels tick_label_formatter : callable A callable that is passed the numerical value of each tick label and which should return a string. tick_in : int The number of pixels by which the ticks go "into" the plot area tick_out : int The number of pixels by which the ticks extend into the label area tick_visible : bool Are ticks visible at all? tick_interval : 'auto' or float What is the dataspace interval between ticks? orientation : Enum("top", "bottom", "left", "right") The location of the axis relative to the plot. This determines where the axis title is located relative to the axis line. axis_line_visible : bool Is the axis line visible? axis_line_color : color ('color_name' or (red, green, blue, [alpha]) tuple) The color of the axis line axis_line_weight : float The line thickness (in pixels) of the axis line axis_line_style : LineStyle('solid') The dash style of the axis line""" def xaxis(**kwds): """ Configures the x-axis. Usage ----- * ``xaxis()``: toggles the horizontal axis on or off. * ``xaxis(**kwds)``: set parameters of the horizontal axis. %s """ % _axis_params p = curplot() if p: if kwds: p.x_axis.set(**kwds) else: p.x_axis.visible ^= True p.request_redraw() xaxis.__doc__ = """ Configures the x-axis. Usage ----- * ``xaxis()``: toggles the horizontal axis on or off. * ``xaxis(**kwds)``: set parameters of the horizontal axis. %s """ % _axis_params def yaxis(**kwds): """ Configures the y-axis. Usage ----- * ``yaxis()``: toggles the vertical axis on or off. * ``yaxis(**kwds)``: set parameters of the vertical axis. %s """ % _axis_params p = curplot() if p: if kwds: p.y_axis.set(**kwds) else: p.y_axis.visible ^= True p.request_redraw() yaxis.__doc__ = """ Configures the y-axis. Usage ----- * ``yaxis()``: toggles the vertical axis on or off. * ``yaxis(**kwds)``: set parameters of the vertical axis. %s """ % _axis_params def xgrid(): """ Toggles the grid perpendicular to the X axis. """ p = curplot() if p: p.x_grid.visible ^= True p.request_redraw() def ygrid(): """ Toggles the grid perpendicular to the Y axis. """ p = curplot() if p: p.y_grid.visible ^= True p.request_redraw() def _set_scale(axis, system): p = curplot() if p: if axis == 'x': log_linear_trait = 'index_scale' ticks = p.x_ticks else: log_linear_trait = 'value_scale' ticks = p.y_ticks if system == 'time': system = CalendarScaleSystem() if isinstance(system, basestring): setattr(p, log_linear_trait, system) else: if system is None: system = dict(linear=p.linear_scale, log=p.log_scale).get( p.get(log_linear_trait), p.linear_scale) ticks.scale = system p.request_redraw() def xscale(system=None): """ Change the scale system for the X-axis ticks. Usage ----- * ``xscale()``: revert the scale system to the default. * ``xscale('time')``: use the calendar scale system for time series. * ``xscale('log')``: use a generic log-scale. * ``xscale('linear')``: use a generic linear-scale. * ``xscale(some_scale_system)``: use an arbitrary ScaleSystem object. """ _set_scale('x', system) def yscale(system=None): """ Change the scale system for the Y-axis ticks. Usage ----- * ``yscale()``: revert the scale system to the default. * ``yscale('time')``: use the calendar scale system for time series. * ``yscale('log')``: use a generic log-scale. * ``yscale('linear')``: use a generic linear-scale. * ``yscale(some_scale_system)``: use an arbitrary ScaleSystem object. """ _set_scale('y', system) def legend(setting=None): """ Sets or toggles the presence of the legend Usage ----- * ``legend()``: toggles the legend; if it is currently visible, it is hideen, and if it is currently hidden, then it is displayed * ``legend(True)``: shows the legend * ``legend(False)``: hides the legend """ p = curplot() if p: if setting is None: setting = not p.legend.visible p.legend.visible = setting p.request_redraw() #----------------------------------------------------------------------------- # Tools #----------------------------------------------------------------------------- def tool(): """ Toggles tools on and off. """ p = curplot() if p: pass #----------------------------------------------------------------------------- # Saving and IO #----------------------------------------------------------------------------- def save(filename="chacoplot.png", dpi=72, pagesize="letter", dest_box=None, units="inch"): """ Saves the active plot to an file. Currently supported file types are: bmp, png, jpg. """ p = curplot() if not p: print "Doing nothing because there is no active plot." return import os.path ext = os.path.splitext(filename)[-1] if ext == ".pdf": print "Warning: the PDF backend is still a little buggy." from chaco.pdf_graphics_context import PdfPlotGraphicsContext # Set some default PDF options if none are provided if dest_box is None: dest_box = (0.5, 0.5, -0.5, -0.5) gc = PdfPlotGraphicsContext(filename = filename, pagesize = pagesize, dest_box = dest_box, dest_box_units = units) # temporarily turn off the backbuffer for offscreen rendering use_backbuffer = p.use_backbuffer p.use_backbuffer = False gc.render_component(p) p.use_backbuffer = use_backbuffer gc.save() del gc print "Saved to", filename elif ext in [".bmp", ".png", ".jpg"]: from chaco.api import PlotGraphicsContext gc = PlotGraphicsContext(tuple(p.outer_bounds), dpi=dpi) # temporarily turn off the backbuffer for offscreen rendering use_backbuffer = p.use_backbuffer p.use_backbuffer = False gc.render_component(p) p.use_backbuffer = use_backbuffer gc.save(filename) del gc print "Saved to", filename else: print "Format not yet supported:", ext print "Currently supported formats are: bmp, png, jpg." return # EOF chaco-4.5.0/chaco/shell/plot_maker.py0000644000076600000240000003210512426452312020252 0ustar jrocherstaff00000000000000""" Contains the logic behind creating and configuring new plots from a set of user-supplied arguments. """ # Standard library imports import re # Major library imports from numpy import all, array, arange, asarray, reshape, shape, transpose # Chaco imports from chaco.api import (create_line_plot, create_scatter_plot, ArrayDataSource, ImageData) from chaco.tools.api import HighlightTool # Local relative imports from chaco_shell_error import ChacoShellError # Normally I don't define an __all__, but this lets us distinguish # the top level plot-producing functions from the various helper # functions. __all__ = ["do_plot", "do_imshow", "do_pcolor", "do_contour", "do_plotv", "SizeMismatch", ] #----------------------------------------------------------------------------- # Exceptions #----------------------------------------------------------------------------- class SizeMismatch(ChacoShellError): pass #----------------------------------------------------------------------------- # Utility functions #----------------------------------------------------------------------------- def is1D (a): s = shape(a) return ((len(s) == 1) or (s[0] == 1) or (s[1] == 1)) def is2D (a): return (len(shape(a)) == 2) def row ( a ): return reshape( asarray( a ), [1,-1] ) def col ( a ): return reshape( asarray( a ), [-1,1] ) #----------------------------------------------------------------------------- # Plot commands for chaco-style plotv() #----------------------------------------------------------------------------- def do_plotv(session, *args, **kw): """ Creates a list of plots from the data in ``*args`` and options in ``**kw``, according to the docstring on commands.plot(). """ sort = kw.get("sort", "none") sources_list = make_data_sources(session, sort, *args) plot_type = kw.get("type", "line") if plot_type == "scatter": plots = [create_scatter_plot(sources) for sources in sources_list] elif plot_type == "line": plots = [create_line_plot(sources) for sources in sources_list] else: raise ChacoShellError, "Unknown plot type '%s'." % plot_type for plot in plots: plot.orientation = kw.get("orientation", "h") return plots def make_data_sources(session, index_sort="none", *args): """ Given a list of arguments, returns a list of (index, value) datasources to create plots from. """ # Make sure everything is a numpy array data = [] for arg in args: if isinstance(arg, list) or isinstance(arg, tuple): data.append(array(arg)) else: data.append(arg) if len(data) == 0: raise ChacoShellError, "Insufficient data for plot." # 1D array(s) if len(data[0].shape) == 1: if len(data) == 1: # Only a single array was provided index_ds = ArrayDataSource(arange(len(data[0])), sort_order="ascending") value_ds = ArrayDataSource(data[0], sort_order="none") return [(index_ds, value_ds)] else: # multiple arrays were provided index_ds = ArrayDataSource(data[0], sort_order=index_sort) return [(index_ds, ArrayDataSource(v, sort_order="none")) for v in data[1:]] # 2D arrays elif len(data[0].shape) == 2: sources = [] # Loop over all the 2D arrays for ary in data: if ary.shape[0] > ary.shape[1]: index_ary = ary[:, 0] value_arrays = ary[:, 1:] else: index_ary = ary[0] value_arrays = transpose(ary[1:]) index_ds = ArrayDataSource(index_ary, sort_order=index_sort) sources.extend([(index_ds, ArrayDataSource(v, sort_order="none")) for v in value_arrays]) return sources # Not a two-dimensional array, error. else: raise ChacoShellError, "Unable to create plot data sources from array of" \ "shape " + str(data[1].shape) + "." #----------------------------------------------------------------------------- # Plot commands for matlab-compatible plot() function #----------------------------------------------------------------------------- # Regular expressions for parsing the format string color_re = re.compile('[ymcrgbwk]') color_trans = { 'y': 'yellow', 'm': 'magenta', 'c': 'cyan', 'r': 'red', 'g': 'green', 'b': 'blue', 'w': 'white', 'k': 'black' } # This one isn't quite right: marker_re = re.compile('[ox+s^v]|(?:[^-])[.]') marker_trans = { '.': 'dot', 'o': 'circle', 'x': 'cross', '+': 'plus', 's': 'square', '^': 'triangle', 'v': 'inverted_triangle' } line_re = re.compile('--|-\.|[-:]') line_trans = { '-': 'solid', ':': 'dot', '-.': 'dot dash', '--': 'dash' } def _process_format(format): """ Converts a format string into a (color, line, marker, marker_color) tuple. """ if format == '': return ('black', 'solid', None, None) color, line, marker, marker_color = 'black', None, None, None m = color_re.findall(format) if len(m) > 0: color = marker_color = color_trans[m[0]] if len(m) > 1: marker_color = color_trans[m[1]] m = marker_re.findall(format) # The -1 takes care of 'r.', etc: if len(m) > 0: marker = marker_trans[m[0][-1]] m = line_re.findall(format) if len(m): line = line_trans[m[0]] return (color, line, marker, marker_color) def _process_group(group, plot_data=None): """ Returns a (x_1D, y_1D, format_str) tuple from an input tuple of 1 to 3 elements: (x,y,format_str). A PlotData object can be optionally provided to disambiguate the cases when exactly two strings are passed in. The two strings could be the names of the x and y datasources, or they could be the name of the y datasource and a format string. By checking the second string against the plot_data's list of datasources, the method can determine what it is meant to be. """ # Interpret and split the 'group' tuple into x, y, and plotinfo plotinfo = '' if len(group) == 1: y = group[0] y_data = plot_data.get_data(y) x = plot_data.set_data("", arange(len(y_data)), generate_name=True) elif len(group) == 2: # There are two possibilities here; a single y was provided along # with a format string, or an x and y were provided. If PlotData # was provided, use that to disambiguate; otherwise, assume that the # second string is a format string. if isinstance(group[1], basestring): if plot_data and group[1] in plot_data.list_data(): x = group[0] y = group[1] else: plotinfo = group[1] y = group[0] y_data = plot_data.get_data(y) x = plot_data.set_data("", arange(len(y_data)), generate_name=True) else: x, y = group elif len(group) == 3: x, y, plotinfo = group else: raise ChacoShellError("Found too many elements in group while" \ " constructing plot.") return x, y, plotinfo def _check_sort_order(data): diffs = data[1:] - data[:-1] if all(diffs >= 0): return "ascending" elif all(diffs <= 0): return "descending" else: return "none" def do_plot(plotdata, active_plot, *data_and_formats, **kwtraits): """ Takes a list of data (arrays or names) and format string arguments and creates new plots on the active_plot. Returns a list of plot names on the active plot. """ # The list of data and formats is broken up by format strings, # so we break it up by arguments that are strings. cur_group = [] groups = [] valid_names = plotdata.list_data() for arg in data_and_formats: if not isinstance(arg, basestring): # an array was passed in cur_group.append(plotdata.set_data("", arg, generate_name=True)) elif arg in valid_names: # the name of an existing plotdata item was passed in cur_group.append(arg) else: # String that is not in plotdata is interpreted as a format # string, thereby terminating this group cur_group.append(arg) groups.append(cur_group) cur_group = [] if len(cur_group) > 0: groups.append(cur_group) # Process the list of groups and create a list of plots; # broadcast the keyword traits to all of them. plots = [] for group in groups: x, y, format_str = _process_group(group, plot_data = plotdata) linecolor, line, marker, markercolor = _process_format(format_str) plot_type = [] format = kwtraits.copy() if line is not None: plot_type.append("line") format["line_style"] = line format["color"] = linecolor if marker is not None: plot_type.append("scatter") format["marker"] = marker format["color"] = markercolor x_sort_order = _check_sort_order(plotdata.get_data(x)) plots.extend(active_plot.plot((x,y), type=",".join(plot_type), **format)) # Set the sort order x_ds = active_plot.datasources[x] if isinstance(x_ds, ArrayDataSource): x_ds.sort_order = x_sort_order # Check to see if the active_plot has a highlighter tool already; if not, # then add it. for tool in active_plot.tools: if isinstance(tool, HighlightTool): break else: active_plot.tools.append(HighlightTool(active_plot)) return plots def do_imread(*data, **kwargs): """ Returns image file as array. """ # Check to see if the data given is either a file path or a file object if isinstance(data[0], basestring) or isinstance(data[0], file): return ImageData.fromfile(data[0]) else: raise ValueError("do_imread takes a string filename") def do_imshow(plotdata, active_plot, *data, **kwargs): """ Creates an image plot on the active plot, given either a filename or data. """ if len(data) != 1: raise ValueError("do_imshow takes one data source") x = None y = None try: z = _get_or_create_plot_data(data[0], plotdata) except ValueError: # z is the name of the file # create plot data image = do_imread(data[0], *data, **kwargs) z = plotdata.set_data("", image, generate_name=True) plot_list = [active_plot.img_plot(z, xbounds=x, ybounds=y, **kwargs)] return plot_list def do_pcolor(plotdata, colormap, active_plot, *data, **kwargs ): """ Creates a pseudocolor image plot on the active plot, given a 2-D scalar data and a colormap. """ # if we get just one data source, it is assumed to be the scalar field if len(data) == 1: x = None y = None z = _get_or_create_plot_data(data[0], plotdata) # three data sources means we got x-y grid data of some sort, too elif len(data) == 3: x = _get_or_create_plot_data(data[0], plotdata) y = _get_or_create_plot_data(data[1], plotdata) z = _get_or_create_plot_data(data[2], plotdata) else: raise ValueError("do_pcolor takes one or three data sources") plot_list = [active_plot.img_plot(z, xbounds=x, ybounds=y, colormap=colormap, **kwargs)] return plot_list def do_contour(plotdata, colormap, active_plot, type, *data, **kwargs ): """ Creates a contour plot on the active plot, given a 2-D scalar data and a colormap. """ # if we get just one data source, it is assumed to be the scalar field if len(data) == 1: x = None y = None z = _get_or_create_plot_data(data[0], plotdata) # three data sources means we got x-y grid data of some sort, too elif len(data) == 3: x = _get_or_create_plot_data(data[0], plotdata) y = _get_or_create_plot_data(data[1], plotdata) z = _get_or_create_plot_data(data[2], plotdata) else: raise ValueError("do_contour takes one or three data sources") # we have to do slightly different calls here because of the different # handling of colormaps if type is 'poly': plot_list = [active_plot.contour_plot(z, type, xbounds=x, ybounds=y, poly_cmap=colormap, **kwargs)] else: plot_list = [active_plot.contour_plot(z, type, xbounds=x, ybounds=y, colors=colormap, **kwargs)] return plot_list def _get_or_create_plot_data(data, plotdata): """Create a new name for `data` if necessary, or check it is a valid name. """ valid_names = plotdata.list_data() if not isinstance(data, basestring): name = plotdata.set_data("", data, generate_name=True) else: if data not in valid_names: msg = '{} is not an existing name for plot data' raise ValueError(msg.format(data)) name = data return name # EOF chaco-4.5.0/chaco/shell/plot_window.py0000644000076600000240000001764412426452312020475 0ustar jrocherstaff00000000000000""" Defines the PlotWindow class. """ from enable.api import Window from chaco.shell.scaly_plot import ScalyPlot from traits.etsconfig.api import ETSConfig if ETSConfig.toolkit == "wx": import wx class PlotWindow(wx.Frame): """ A window for holding top-level plot containers. Contains many utility methods for controlling the appearance of the window, which mostly pass through to underlying WX calls. """ def __init__(self, is_image=False, bgcolor="white", image_default_origin="top left", *args, **kw): kw.setdefault("size", (600,600)) wx.Frame.__init__(self, None, *args, **kw ) # Some defaults which should be overridden by preferences. self.bgcolor = bgcolor self.image_default_origin = image_default_origin # Create an empty top-level container if is_image: top_container = self._create_top_img_container() else: top_container = self._create_top_container() # The PlotSession of which we are a part. We need to know this in order # to notify it of our being closed, etc. self.session = None # Create the Enable Window object, and store a reference to it. # (This will be handy later.) The Window requires a WX parent object # as its first argument, so we just pass 'self'. self.plot_window = Window(self, component=top_container) # We'll create a default sizer to put our plot_window in. sizer = wx.BoxSizer(wx.HORIZONTAL) # Since Window is an Enable object, we need to get its corresponding # WX control. This is stored in its ".control" attribute. sizer.Add(self.plot_window.control, 1, wx.EXPAND) # Hook up event handlers for destroy, etc. wx.EVT_WINDOW_DESTROY(self, self._on_window_close) # More WX boilerplate. self.SetSizer(sizer) self.SetAutoLayout(True) self.Show(True) return def get_container(self): return self.plot_window.component def set_container(self, container): self.plot_window.component = container def iconize(self, iconize): """Iconizes the window if *iconize* is True. """ self.Iconize(iconize) def maximize(self, maximize): """ If *maximize* is True, maximizes the window size; restores if False. """ self.Maximize(maximize) def set_size(self, width, height): self.SetSize((width, height)) def set_title(self, title): self.SetTitle(title) def raise_window(self): """Raises this window to the top of the window hierarchy. """ self.Raise() def close(self): self.Close() # This is a Python property because this is not a HasTraits subclass. container = property(get_container, set_container) #------------------------------------------------------------------------ # Private methods #------------------------------------------------------------------------ def _create_top_container(self): plot = ScalyPlot( padding=50, fill_padding=True, bgcolor=self.bgcolor, use_backbuffer=True, ) return plot def _create_top_img_container(self): plot = ScalyPlot( padding=50, fill_padding=True, bgcolor=self.bgcolor, use_backbuffer=True, default_origin=self.image_default_origin, ) return plot def _on_window_close(self, event): if self.session: try: ndx = self.session.windows.index(self) self.session.del_window(ndx) except ValueError: pass elif ETSConfig.toolkit == "qt4": from pyface.qt import QtCore, QtGui class PlotWindow(QtGui.QFrame): """ A window for holding top-level plot containers. Contains many utility methods for controlling the appearance of the window, which mostly pass through to underlying Qt calls. """ def __init__(self, is_image=False, bgcolor="white", image_default_origin="top left", *args, **kw): if 'size' in kw and isinstance(kw['size'], tuple): # Convert to a QSize. kw['size'] = QtCore.QSize(*kw['size']) super(PlotWindow, self).__init__(None, *args, **kw ) # Some defaults which should be overridden by preferences. self.bgcolor = bgcolor self.image_default_origin = image_default_origin # Create an empty top-level container if is_image: top_container = self._create_top_img_container() else: top_container = self._create_top_container() # The PlotSession of which we are a part. We need to know this in order # to notify it of our being closed, etc. self.session = None # Create the Enable Window object, and store a reference to it. # (This will be handy later.) The Window requires a parent object # as its first argument, so we just pass 'self'. self.plot_window = Window(self, component=top_container) layout = QtGui.QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self.plot_window.control) self.setLayout(layout) size = kw.get("size", QtCore.QSize(600,600)) self.set_size(size.width(), size.height()) self.show() def get_container(self): return self.plot_window.component def set_container(self, container): self.plot_window.component = container def iconize(self, iconize): """Iconizes the window if *iconize* is True. """ if iconize: self.showMinimized() else: self.showNormal() def maximize(self, maximize): """ If *maximize* is True, maximizes the window size; restores if False. """ if maximize: self.showMaximized() else: self.showNormal() def set_size(self, width, height): self.resize(width, height) def set_title(self, title): self.setWindowTitle(title) def raise_window(self): """Raises this window to the top of the window hierarchy. """ self.raise_() # This is a Python property because this is not a HasTraits subclass. container = property(get_container, set_container) #------------------------------------------------------------------------ # Private methods #------------------------------------------------------------------------ def _create_top_container(self): plot = ScalyPlot( padding=50, fill_padding=True, bgcolor=self.bgcolor, use_backbuffer=True, ) return plot def _create_top_img_container(self): plot = ScalyPlot( padding=50, fill_padding=True, bgcolor=self.bgcolor, use_backbuffer=True, default_origin=self.image_default_origin, ) return plot def closeEvent(self, event): if self.session: try: ndx = self.session.windows.index(self) self.session.del_window(ndx) except ValueError: pass else: pass # EOF chaco-4.5.0/chaco/shell/preferences.py0000644000076600000240000000237612426452312020425 0ustar jrocherstaff00000000000000""" Defines the Preferences class for the Chaco shell. """ from enable.api import white_color_trait from traits.api import Enum, HasTraits, Int, Str class Preferences(HasTraits): """ Contains all the preferences that configure the Chaco shell session. """ # Width of the plot window, in pixels. window_width = Int(600) # Height of the plot window, in pixels. window_height = Int(600) # The type of plot to display. plot_type = Enum("line", "scatter") # Default name to use for the plot window. default_window_name = Str("Chaco Plot") # The default background color. bgcolor = white_color_trait # The default location of the origin for new image plots image_default_origin = Enum("top left", "bottom left", "bottom right", "top right") @classmethod def from_file(cls, filename): """ Creates a new preferences object from a file on disk. """ prefs = cls() prefs.load(filename) return prefs def load(self, filename): """ Loads a preferences file; existing settings are overwritten. """ pass def save(self, filename): """ Saves the preferences to *filename*. """ pass # EOF chaco-4.5.0/chaco/shell/scaly_plot.py0000644000076600000240000001054012426452312020265 0ustar jrocherstaff00000000000000""" A Plot which uses ScaleSystems for its ticks. """ from traits.api import Any from chaco.api import (DataRange2D, LinearMapper, LogMapper, PlotGrid, Plot, PlotAxis) from chaco.scales_tick_generator import ScalesTickGenerator from chaco.scales.api import DefaultScale, LogScale, ScaleSystem def add_default_axes(plot, orientation="normal", vtitle="", htitle=""): """ Creates left and bottom axes for a plot. Assumes that the index is horizontal and value is vertical by default; set orientation to something other than "normal" if they are flipped. """ if orientation in ("normal", "h"): v_mapper = plot.value_mapper h_mapper = plot.index_mapper else: v_mapper = plot.index_mapper h_mapper = plot.value_mapper yticks = ScalesTickGenerator() left = PlotAxis( orientation='left', title=vtitle, mapper=v_mapper, component=plot, tick_generator=yticks, ) xticks = ScalesTickGenerator() bottom = PlotAxis( orientation='bottom', title=htitle, mapper=h_mapper, component=plot, tick_generator=xticks, ) plot.underlays.append(left) plot.underlays.append(bottom) return left, bottom class ScalyPlot(Plot): x_axis = Any() y_axis = Any() x_ticks = Any() y_ticks = Any() linear_scale_factory = Any() log_scale_factory = Any() def _linear_scale_default(self): return self._make_scale("linear") def _log_scale_default(self): return self._make_scale("log") def _make_scale(self, scale_type="linear"): """ Returns a new linear or log scale """ if scale_type == "linear": if self.linear_scale_factory is not None: return self.linear_scale_factory() else: return ScaleSystem(DefaultScale()) else: if self.log_scale_factory is not None: return self.log_scale_factory() else: return ScaleSystem(LogScale()) def _init_components(self): # Since this is called after the HasTraits constructor, we have to make # sure that we don't blow away any components that the caller may have # already set. if self.range2d is None: self.range2d = DataRange2D() if self.index_mapper is None: if self.index_scale == "linear": imap = LinearMapper(range=self.range2d.x_range) else: imap = LogMapper(range=self.range2d.x_range) self.index_mapper = imap if self.value_mapper is None: if self.value_scale == "linear": vmap = LinearMapper(range=self.range2d.y_range) else: vmap = LogMapper(range=self.range2d.y_range) self.value_mapper = vmap if self.x_ticks is None: self.x_ticks = ScalesTickGenerator(scale=self._make_scale(self.index_scale)) if self.y_ticks is None: self.y_ticks = ScalesTickGenerator(scale=self._make_scale(self.value_scale)) if self.x_grid is None: self.x_grid = PlotGrid(mapper=self.x_mapper, orientation="vertical", line_color="lightgray", line_style="dot", component=self, tick_generator=self.x_ticks) if self.y_grid is None: self.y_grid = PlotGrid(mapper=self.y_mapper, orientation="horizontal", line_color="lightgray", line_style="dot", component=self, tick_generator=self.y_ticks) if self.x_axis is None: self.x_axis = PlotAxis(mapper=self.x_mapper, orientation="bottom", component=self, tick_generator=self.x_ticks) if self.y_axis is None: self.y_axis = PlotAxis(mapper=self.y_mapper, orientation="left", component=self, tick_generator=self.y_ticks) def _index_scale_changed(self, old, new): Plot._index_scale_changed(self, old, new) # Now adjust the ScaleSystems. self.x_ticks.scale = self._make_scale(self.index_scale) def _value_scale_changed(self, old, new): Plot._value_scale_changed(self, old, new) # Now adjust the ScaleSystems. self.y_ticks.scale = self._make_scale(self.value_scale) chaco-4.5.0/chaco/shell/session.py0000644000076600000240000001146712426452312017610 0ustar jrocherstaff00000000000000""" Defines the PlotSession class. """ # Enthoght library imports from chaco.array_plot_data import ArrayPlotData from chaco.default_colormaps import * from traits.api import Any, Bool, Dict, HasTraits, Instance, Int, \ List, Property, Trait, Str # Local, relative imports from plot_window import PlotWindow from preferences import Preferences class PlotSession(HasTraits): """ Encapsulates all of the session-level globals, including preferences, windows, etc. """ # The preferences object in effect for this session. prefs = Instance(Preferences, args=()) # The list of currently active windows. windows = List(PlotWindow) # A dict mapping names to windows. window_map = Dict(Str, PlotWindow) # The current hold state. hold = Bool(False) # The session holds a single ArrayPlotData instance to which it adds unnamed # arrays that are provided to various plotting commands. data = Instance(ArrayPlotData, args=()) #------------------------------------------------------------------------ # "active" pointers #------------------------------------------------------------------------ # The index of the active window. active_window_index = Trait(None, None, Int) # The active window. active_window = Property # The active colormap. colormap = Trait(jet, Any) def new_window(self, name=None, title=None, is_image=False): """Creates a new window and returns the index into the **windows** list for the new window. """ new_win = PlotWindow( is_image=is_image, size=(self.prefs.window_width, self.prefs.window_height), bgcolor=self.prefs.bgcolor, image_default_origin=self.prefs.image_default_origin, ) new_win.data = self.data new_win.get_container().data = self.data new_win.session = self if title is not None: new_win.set_title(title) elif name != None: new_win.set_title(name) else: new_win.set_title(self.prefs.default_window_name) self.windows.append(new_win) if name != None: self.window_map[name] = new_win return len(self.windows)-1 def get_window(self, ident): """ Retrieves a window either by index or by name """ if isinstance(ident, basestring): return self.window_map.get(ident, None) elif type(ident) == int and ident < len(self.windows): return self.windows[ident] else: return None def del_window(self, ident): """ Deletes the specified window. Parameters ---------- ident : string or number The name of the window in **window_map**, or the index of the window in **windows**. """ if isinstance(ident, basestring): if ident in self.window_map: win = self.window_map[ident] del self.window_map[ident] else: return elif type(ident) == int: if ident >= len(self.windows): print "No such window %d." % ident win = self.windows.pop(ident) if len(self.windows) == 0: self.active_window = None elif self.active_window_index >= ident: self.active_window_index -= 1 if win in self.window_map.values(): # we have to go through the whole dict and remove all keys # that correspond to the deleted window for k, v in self.window_map.items(): if v == win: del self.window_map[k] else: return def _get_active_window(self): if self.active_window_index is not None: return self.windows[self.active_window_index] else: return None def _set_active_window(self, win): if win in self.windows: self.active_window_index = self.windows.index(win) elif win is None: self.active_window_index = None else: raise RuntimeError, "That window is not part of this session." def _colormap_changed(self): plots = [] for w in self.windows: container = w.get_container() for vals in container.plots.values(): plots.extend(vals) for p in plots: if hasattr(p, "color_mapper"): p.color_mapper = self.colormap(p.color_mapper.range) p.invalidate_draw() p.request_redraw() elif hasattr(p, "colors"): if isinstance(p.colors, basestring) or \ isinstance(p.colors, AbstractColormap): p.colors = color_map_dict[self.colormap] # EOF chaco-4.5.0/chaco/simple_plot_frame.py0000644000076600000240000001202212426452312020503 0ustar jrocherstaff00000000000000""" Defines the (deprecated) SimplePlotFrame class. """ ################################################################################# # # NOTE: PlotFrames are deprecated. There is no need to use them any more. # This class will be removed sometime in the near future. # ################################################################################# from __future__ import with_statement # Enthought library imports from traits.api import Bool # Local, relative imports from base_plot_frame import BasePlotFrame from plot_containers import OverlayPlotContainer class SimplePlotFrame(BasePlotFrame): """ A plot frame with just a single, center container that takes up the entire frame. NOTE: PlotFrames are deprecated. There is no need to use them any more. This class will be removed sometime in the future. """ # This frame has only one position for plot components. Overrides # PlotFrame. slot_names = ("center") # Default width and height. Class attribute. default_bounds = (500, 500) # This frame does not resize to fit components. Overrides PlotFrame. fit_components = "" # This frame maximizes itself within the window, if it is a top-level # component. Overrides Enable Container. fit_window = True #------------------------------------------------------------------------ # Private traits #------------------------------------------------------------------------ # Does the component need to do a layout call? _layout_needed = Bool(True) def __init__(self, **kwtraits): # Delay setting the bounds until after base class initialization if kwtraits.has_key("bounds"): bounds = kwtraits.pop("bounds") else: bounds = list(self.default_bounds) BasePlotFrame.__init__(self, **kwtraits) self.set_slot("center", OverlayPlotContainer(resizable="hv")) self.bounds = bounds return #------------------------------------------------------------------------ # Protected methods #------------------------------------------------------------------------ def _draw_component(self, gc, view_bounds=None, mode="normal"): """ Draws the component. This method is preserved for backwards compatibility with _old_draw(). Overrides PlotComponent. """ with gc: # Translate to our .position, because even though we are supposed # to be a top-level Chaco component, we might still be contained # within other Enable components. gc.translate_ctm(*self.position) with gc: self.center.draw(gc, view_bounds, mode) return def get_preferred_size(self): """ Returns the size (width,height) that is preferred for this component. Overrides PlotComponent. """ size = [0,0] component_pref_size = None if "h" not in self.resizable: if "h" in self.fit_components: component_pref_size = self.center.get_preferred_size() size[0] = component_pref_size[0] else: size[0] = self.default_bounds[0] if "v" not in self.resizable: if "v" in self.fit_components: if not component_pref_size: component_pref_size = self.center.get_preferred_size() size[1] = component_pref_size[1] else: size[1] = self.default_bounds[1] return size def _do_layout(self): """ Performs a layout and sets the size and positions on this frame's containers, given its width and height. """ component = self.center preferred_size = None if "h" in component.resizable: component.outer_width = self.width elif "h" in self.fit_components: preferred_size = component.get_preferred_size() component.outer_width = preferred_size[0] self.width = preferred_size[0] else: # We are not autosizing to our component, and it's not going to # auto-size to our bounds, so do nothing. pass if "v" in component.resizable: component.outer_height = self.height elif "v" in self.fit_components: if preferred_size is None: preferred_size = component.get_preferred_size() component.outer_height = preferred_size[1] self.height = preferred_size[1] else: # We are not autosizing to our component, and it's not going to # auto-size to our bounds, so do nothing. pass component.outer_position = [0,0] component.do_layout() return ### Persistence ########################################################### #_pickles = () def __getstate__(self): state = super(SimplePlotFrame,self).__getstate__() for key in ['_layout_needed']: if state.has_key(key): del state[key] return state # EOF chaco-4.5.0/chaco/speedups.py0000644000076600000240000000322512426452312016637 0ustar jrocherstaff00000000000000 # This contains python implementations of all the speedups from _speedups_fallback import * # cython implementation of speedups. Import these if we can. try: from _cython_speedups import * except ImportError: pass #try: # from numpy import asarray # import _speedups # def scatterplot_gather_points(index, index_low, index_high, # value, value_low, value_high, # index_mask=None, index_sel=None, index_sel_mask=None, # value_mask=None, value_sel=None, value_sel_mask=None): # if index_low is None: # index_low = -inf # if index_high is None: # index_high = inf # if value_low is None: # value_low = -inf # if value_high is None: # value_high = inf # if index_mask is not None: # index_mask = asarray(index_mask, dtype=bool) # if value_mask is not None: # value_mask = asarray(value_mask, dtype=bool) # if index_sel_mask is not None: # index_sel_mask = asarray(index_sel_mask, dtype=bool) # if value_sel_mask is not None: # value_sel_mask = asarray(value_sel_mask, dtype=bool) # return _speedups.scatterplot_gather_points(index, index_low, index_high, # value, value_low, value_high, # index_mask=index_mask, index_sel=index_sel, # index_sel_mask=index_sel_mask, # value_mask=value_mask, value_sel=value_sel, # value_sel_mask=value_sel_mask) #except ImportError: # from _speedups_fallback import * chaco-4.5.0/chaco/subdivision_cells.py0000644000076600000240000001706112426452312020532 0ustar jrocherstaff00000000000000""" Defines cell-related classes and functions. """ from numpy import take, array, concatenate, nonzero from traits.api import HasStrictTraits, Instance, Delegate, Array, List, \ Tuple, Property, Trait, Any, Disallow from datamapper import AbstractDataMapper, right_shift, left_shift, sort_points def find_runs(int_array, order='ascending'): """ find_runs(int_array, order=<'ascending'|'flat'|'descending'>) -> list_of_int_arrays Given an integer array sorted in ascending, descending, or flat order, this function returns a list of continuous runs of integers inside the list. For example:: find_runs([1,2,3,6,7,8,9,10,11,15]) returns [ [1,2,3], [6,7,8,9,10,11], [15] ] and:: find_runs([0,0,0,1,1,1,1,0,0,0,0]) returns [ [0,0,0], [1,1,1,1], [0,0,0,0] ] """ ranges = arg_find_runs(int_array, order) if ranges: return [int_array[i:j] for (i,j) in ranges] else: return [] def arg_find_runs(int_array, order='ascending'): """ This function is like find_runs(), but it returns a list of tuples indicating the start and end indices of runs in the input *int_array*. """ if len(int_array) == 0: return [] assert len(int_array.shape)==1, "find_runs() requires a 1D integer array." if order == 'ascending': increment = 1 elif order == 'descending': increment = -1 else: increment = 0 rshifted = right_shift(int_array, int_array[0]-increment) start_indices = concatenate([[0], nonzero(int_array - (rshifted+increment))[0]]) end_indices = left_shift(start_indices, len(int_array)) return zip(start_indices, end_indices) class AbstractCell(HasStrictTraits): """ Abstract class for grid cells in a uniform subdivision. Individual subclasses store points in different, possibly optimized fashion, and performance may be drastically different between different cell subclasses for a given set of data. """ # The parent of this cell. parent = Instance(AbstractDataMapper) # The sort traits characterizes the internal points list. _sort_order = Delegate('parent') # The point array for this cell. This attribute delegates to parent._data, # which references the actual point array. For the sake of simplicity, # cells assume that _data is sorted in fashion indicated by **_sort_order**. # If this doesn't hold, then each cell needs to have its own duplicate # copy of the sorted data. data = Delegate('parent', '_data') # A list of indices into **data** that reflect the points inside this cell. indices = Property # Shadow trait for **indices**. _indices = Any def add_indices(self, indices): """ Adds a list of integer indices to the existing list of indices. """ raise NotImplementedError def get_points(self): """ Returns a list of points that was previously set. This operation might be large and expensive; in general, use _get_indices() instead. """ raise NotImplementedError def reverse_indices(self): """ Tells the cell to manipulate its indices so that they index to the same values in a reversed data array. Generally this method handles the situation when the parent's _data array has been flipped due to a sort order change. The length of _data must not have changed; otherwise there is no way to know the proper way to manipulate indices. """ raise NotImplementedError def _set_indices(self, indices): raise NotImplementedError def _get_indices(self): """ Returns the list of indices into _data that reflect the points inside this cell. """ raise NotImplementedError class Cell(AbstractCell): """ A basic cell that stores its point indices as an array of integers. """ # A list of indices into **data** that reflect the points inside this cell # (overrides AbstractCell). indices = Property(Array) # A 1-D array of indices into _data. _indices = Array def __init__(self, **kw): self._indices = array([]) super(AbstractCell, self).__init__(**kw) def add_indices(self, indices): """ Adds a list of integer indices to the existing list of indices. Implements AbstractCell. """ self._indices = concatenate([self._indices, indices]) return def get_points(self): """ Returns a list of points that was previously set. Implements AbstractCell. """ return take(self.data, self._indices) def reverse_indices(self): """ Tells the cell to manipulate its indices so that they index to the same values in a reversed data array. Implements AbstractCell. """ length = len(self.data) self._indices = [length-i-1 for i in self._indices] return def _set_indices(self, indices): self._indices = indices return def _get_indices(self): return self._indices class RangedCell(AbstractCell): """ A cell optimized for storing lists of continuous points. Rather than storing each individual point index as an element in an array, RangedCell stores a list of index ranges; each range is a (start,end) tuple). """ # A list of indices into **data** that reflect the points inside this cell # (overrides AbstractCell). indices = Property # Don't use the _indices shadow trait; rather, the getters and setters # for 'index' procedurally generate indices from **ranges**. _indices = Disallow # Ranges are an additional interface on RangedCells. ranges = Property(List(Tuple)) # Shadow trait for ranges. _ranges = List(Tuple) #--------------------------------------------------------------------- # AbstractCell methods #--------------------------------------------------------------------- def add_indices(self, indices): """ Adds a list of integer indices to the existing list of indices. Implements AbstractCell. """ self.add_ranges(find_runs(indices)) return def get_points(self): """ Returns a list of points that was previously set. Implements AbstractCell. """ return take(self.data, self.indices) def reverse_indices(self): """ Tells the cell to manipulate its indices so that they index to the same values in a reversed data array. Implements AbstractCell. """ length = len(self.data) self._ranges = [(length-end-1, length-start-1) for (start,end) in self._ranges] return def _set_indices(self, indices): self._ranges = find_runs(indices) return def _get_indices(self): list_of_indices = [range(i,j) for (i,j) in self._ranges] return sum(list_of_indices, []) #--------------------------------------------------------------------- # additional RangedCell methods #--------------------------------------------------------------------- def get_ranges(self): """ Returns a list of tuples representing the (start,end) indices of continuous ranges of points in self._data. """ return self._ranges() def add_ranges(self, ranges): """ Adds a list of ranges ((start,end) tuples) to the current list. This method doesn't check for duplicate or overlapping ranges. """ if self._ranges: self._ranges.extend(ranges) else: self._ranges = ranges return #EOF chaco-4.5.0/chaco/subdivision_mapper.py0000644000076600000240000002547712426452312020726 0ustar jrocherstaff00000000000000""" Defines the SubdivisionDataMapper and SubdivisionLineDataMapper classes. """ # Major library imports import math from numpy import array, arange, concatenate, searchsorted, nonzero, transpose, \ argsort, zeros, sort, vstack import numpy # Enthought library imports from traits.api import List, Array, Tuple, Int, Float # Local, relative imports from datamapper import AbstractDataMapper, right_shift, left_shift, \ sort_points, ArraySortTrait, \ array_zip from subdivision_cells import AbstractCell, Cell, RangedCell, find_runs, \ arg_find_runs class SubdivisionDataMapper(AbstractDataMapper): """ A data mapper that uses a uniform grid of rectangular cells. It doesn't make any assumptions about the continuity of the input data set, and explicitly stores each point in the data set in its cell. If the incoming data is ordered in some fashion such that most cells end up with large ranges of data, then it's better to use the SubdivisionLineDataMapper subclass. """ celltype = Cell _last_region = List(Tuple) _cellgrid = Array # a Numeric array of Cell objects _points_per_cell = Int(100) # number of datapoints/cell to shoot for _cell_lefts = Array # locations of left edge for all cells _cell_bottoms = Array # locations of bottom edge for all cells _cell_extents = Tuple(Float, Float) # the width and height of a cell #------------------------------------------------------------------- # Public AbstractDataMapper methods #------------------------------------------------------------------- def get_points_near(self, pointlist, radius=0.0): if radius != 0: # tmp is a list of list of arrays d = 2*radius cell_points = [ self.get_points_in_rect((px-radius,py-radius,d,d)) for (px,py) in pointlist ] else: indices = self._get_indices_for_points(pointlist) cells = [self._cellgrid[i,j] for (i,j) in indices] self._last_region = self._cells_to_rects(indices) # unique-ify the list of cells cell_points = [c.get_points() for c in set(cells)] return vstack(cell_points) def get_points_in_rect(self, rect): x_span = (rect[0], rect[0]+rect[2]) y_span = (rect[1], rect[1]+rect[3]) min_i, max_i = searchsorted(self._cell_lefts, x_span) - 1 min_j, max_j = searchsorted(self._cell_bottoms, y_span) - 1 cellpts = [ self._cellgrid[i,j].get_points() for i in range(min_i, max_i+1) \ for j in range(min_j, max_j+1) ] self._last_region = ( self._cell_lefts[min_i], self._cell_bottoms[min_j], \ (max_i - min_i + 1) * self._cell_extents[0], \ (max_j - min_j + 1) * self._cell_extents[1] ) return vstack(cellpts) def get_last_region(self): return self._last_region #------------------------------------------------------------------- # AbstractDataMapper's abstract private methods #------------------------------------------------------------------- def _update_datamap(self): self._last_region = [] # Create a new grid of the appropriate size, initialize it with new # Cell instance (of type self.celltype), and perform point insertion # on the new data. if self._data is None: self._cellgrid = array([], dtype=object) self._cell_lefts = array([]) self._cell_bottoms = array([]) else: num_x_cells, num_y_cells = self._calc_grid_dimensions() self._cellgrid = zeros((num_x_cells, num_y_cells), dtype=object) for i in range(num_x_cells): for j in range(num_y_cells): self._cellgrid[i,j] = self.celltype(parent=self) ll, ur = self._extents cell_width = ur[0]/num_x_cells cell_height = ur[1]/num_y_cells # calculate the left and bottom edges of all the cells and store # them in two arrays self._cell_lefts = arange(ll[0], ll[0]+ur[0]-cell_width/2, step=cell_width) self._cell_bottoms = arange(ll[1], ll[1]+ur[1]-cell_height/2, step=cell_height) self._cell_extents = (cell_width, cell_height) # insert the data points self._basic_insertion(self.celltype) return def _clear(self): self._last_region = [] self._cellgrid = [] self._cell_lefts = [] self._cell_bottoms = [] self._cell_extents = (0,0) return def _sort_order_changed(self, old, new): # since trait event notification only happens if the value has changed, # and there are only two types of sorting, it's safe to just reverse our # internal _data object self._data = self._data[::-1] for cell in self._cellgrid: # since cellgrid is a Numeric array, iterating over it produces # a length-1 array cell[0].reverse_indices() return #------------------------------------------------------------------- # helper private methods #------------------------------------------------------------------- def _calc_grid_dimensions(self): numpoints = self._data.shape[0] numcells = numpoints / self._points_per_cell ll, ur = self._extents aspect_ratio = (ur[0]-ll[0]) / (ur[1]-ll[1]) num_y_cells = int(math.sqrt(numcells / aspect_ratio)) num_x_cells = int(aspect_ratio * num_y_cells) if num_y_cells == 0: num_y_cells += 1 if num_y_cells*num_x_cells*self._points_per_cell < numpoints: num_x_cells += 1 return (num_x_cells, num_y_cells) def _basic_insertion(self, celltype): # generate a list of which cell each point in self._data belongs in cell_indices = self._get_indices_for_points(self._data) # We now look for ranges of points belonging to the same cell. # 1. shift lengthwise and difference; runs of cells with the same # (i,j) indices will be zero, and nonzero value for i or j will # indicate a transition to a new cell. (Just like find_runs().) differences = cell_indices[1:] - cell_indices[:-1] # Since nonzero() only works for 1D arrays, we merge the X and Y columns # together to detect any point where either X or Y are nonzero. We have # to add 1 because we shifted cell_indices before differencing (above). diff_indices = nonzero(differences[:,0] + differences[:,1])[0] + 1 start_indices = concatenate([[0], diff_indices]) end_indices = concatenate([diff_indices, [len(self._data)]]) for start,end in zip(start_indices, end_indices): gridx, gridy = cell_indices[start] # can use 'end' here just as well if celltype == RangedCell: self._cellgrid[gridx,gridy].add_ranges([(start,end)]) else: self._cellgrid[gridx,gridy].add_indices(range(start,end)) return def _get_indices_for_points(self, pointlist): """ Given an input Nx2 array of points, returns a list Nx2 corresponding to the column and row indices into the cell grid. """ x_array = searchsorted(self._cell_lefts, pointlist[:,0]) - 1 y_array = searchsorted(self._cell_bottoms, pointlist[:,1]) - 1 return array_zip(x_array, y_array) def _cells_to_rects(self, cells): """ Converts the extents of a list of cell grid coordinates (i,j) into a list of rect tuples (x,y,w,h). The set should be disjoint, but may or may not be minimal. """ # Since this function is generally used to generate clipping regions # or other screen-related graphics, we should try to return large # rectangular blocks if possible. # For now, we just look for horizontal runs and return those. cells = array(cells) y_sorted = sort_points(cells, index=1) # sort acoording to row rownums = sort(array(tuple(set(cells[:,1])))) row_start_indices = searchsorted(y_sorted[:,1], rownums) row_end_indices = left_shift(row_start_indices, len(cells)) rects = [] for rownum, start, end in zip(rownums, row_start_indices, row_end_indices): # y_sorted is sorted by the J (row) coordinate, so after we # extract the column indices, we need to sort them before # passing them to find_runs(). grid_column_indices = sort(y_sorted[start:end][:,0]) #pdb.set_trace() #print grid_column_indices.shape for span in find_runs(grid_column_indices): x = self._cell_lefts[span[0]] y = self._cell_bottoms[rownum] w = (span[-1] - span[0] + 1) * self._cell_extents[0] h = self._cell_extents[1] rects.append((x,y,w,h)) return rects #~ def _array_insertion(self, celltype): #~ # use searchsorted() to determine where the borders split the #~ # data array #~ x_bins = searchsorted(data[:,0], self._cell_lefts[1:]) #~ x_bins_rshift = right_shift(x_bins, 0) #~ grid_x = 0 #~ for x_index_range in zip(x_bins_rshift, x_bins): #~ # now do the same thing in y; the tricky part is remembering #~ # to use axis=1 since everything happens on the y-coordinate #~ columnpoints = data[x_index_range[0] : x_index_range[1]] #~ columnpoints = take(columnpoints, argsort(columnpoints[:,1])) #~ # use searchsorted() to determine where the cell bottoms split the #~ # set of column points. #~ y_bins = searchsorted(columnpoints[:,1], self._cell_bottoms) #~ y_bins_rshift = right_shift(y_bins, 0) #~ grid_y = 0 #~ for startndx, endndx in zip(y_bins_rshift, y_bins): #~ if startndx != endndx: #~ cell = self._cellgrid[grid_x, grid_y] #~ cellpts = columnpoints[startndx:endndx] #~ if cell.sort_order == 'none': #~ cell.points = cellpts #~ elif cell.sort_order == 'ascending': #~ cell.points = find_runs(sort_points(cellpts)) #~ elif cell.sort_order == 'descending': #~ cell.points = find_runs(sort_points(cellpts)[::-1], 'descending') #~ else: #~ raise RuntimeError, "Invalid sort_order: " + cell.sort_order #~ return class SubdivisionLineDataMapper(SubdivisionDataMapper): """ A subdivision data mapper that uses ranged cells. """ celltype = RangedCell #EOF chaco-4.5.0/chaco/svg_graphics_context.py0000644000076600000240000000531612426452312021235 0ustar jrocherstaff00000000000000""" Defines the PlotGraphicsContext class. """ from __future__ import with_statement from kiva.svg import GraphicsContext class SVGGraphicsContext(GraphicsContext): """ A Kiva graphics context, which facilitates rendering plots and plot components into an offscreen or memory buffer. Its only real difference from a Kiva graphics context is that this class correctly offsets the coordinate frame by (0.5, 0.5) and increases the actual size of the image by 1 pixel in each dimension. When rendering into on-screen windows through Enable, this transformation step is handled by Enable. """ # FIXME: Right now this does not resize correctly. (But you shouldn't # resize your GC, anyway!) def __init__(self, size_or_ary, dpi=72.0, *args, **kw): scale = dpi / 72.0 if type(size_or_ary) in (list, tuple) and len(size_or_ary) == 2: size_or_ary = (size_or_ary[0]*scale + 1, size_or_ary[1]*scale + 1) super(SVGGraphicsContext, self).__init__(size_or_ary, *args, **kw) self.translate_ctm(0.5, 0.5) self.scale_ctm(scale, scale) return def render_component(self, component, container_coords=False): """ Renders the given component. Parameters ---------- component : Component The component to be rendered. container_coords : Boolean Whether to use coordinates of the component's container Description ----------- If *container_coords* is False, then the (0,0) coordinate of this graphics context corresponds to the lower-left corner of the component's **outer_bounds**. If *container_coords* is True, then the method draws the component as it appears inside its container, i.e., it treats (0,0) of the graphics context as the lower-left corner of the container's outer bounds. """ x, y = component.outer_position if not container_coords: x = -x y = -y # Compute the new x and y scale from the translation width, height = self.width(), self.height() width_scale = (width - x) / float(width) height_scale = (height - y) / float(height) with self: self.translate_ctm(x, y) self.scale_ctm(width_scale, height_scale) component.draw(self, view_bounds=(0, 0, width, height)) return def clip_to_rect(self, x, y, width, height): """ Offsets the coordinate frame by (0.5, 0.5) and increases the actual size of the image by 1 pixel in each dimension. Overrides Kiva GraphicsContext. """ GraphicsContext.clip_to_rect(self, x-0.5, y-0.5, width+1, height+1) # EOF chaco-4.5.0/chaco/tests/0000755000076600000240000000000012426466422015604 5ustar jrocherstaff00000000000000chaco-4.5.0/chaco/tests/__init__.py0000644000076600000240000000000012426452312017674 0ustar jrocherstaff00000000000000chaco-4.5.0/chaco/tests/_tools.py0000644000076600000240000000170012426452312017444 0ustar jrocherstaff00000000000000from contextlib import contextmanager import sys import traceback # ######### Testing tools @contextmanager def store_exceptions_on_all_threads(): """Context manager that captures all exceptions, even those coming from the UI thread. On exit, the first exception is raised (if any). """ exceptions = [] def excepthook(type, value, tb): exceptions.append(value) message = 'Uncaught exception:\n' message += ''.join(traceback.format_exception(type, value, tb)) sys.stderr.write(message) try: sys.excepthook = excepthook yield finally: if len(exceptions) > 0: raise exceptions[0] sys.excepthook = sys.__excepthook__ @contextmanager def assert_raises(ExceptionClass): try: yield except ExceptionClass: pass else: msg = 'Test should have failed with {}.' raise Exception(msg.format(ExceptionClass.__name__)) chaco-4.5.0/chaco/tests/array_plot_data_test_case.py0000644000076600000240000000317412426452312023353 0ustar jrocherstaff00000000000000import contextlib from traits.testing.unittest_tools import unittest import numpy from chaco.api import ArrayPlotData from traits.api import HasTraits, Instance, List, on_trait_change class ArrayPlotDataEventsCollector(HasTraits): plot_data = Instance(ArrayPlotData) data_changed_events = List @on_trait_change('plot_data:data_changed') def _got_data_changed_event(self, event): self.data_changed_events.append(event) class ArrayPlotDataTestCase(unittest.TestCase): @contextlib.contextmanager def monitor_events(self, plot_data): """ Context manager to collect data_changed events. """ collector = ArrayPlotDataEventsCollector(plot_data=plot_data) yield collector.data_changed_events def test_data_changed_events(self): # Test data. grumpy = numpy.ones((3, 4)) grumpy_too = numpy.zeros(16) plot_data = ArrayPlotData() with self.monitor_events(plot_data) as events: plot_data.set_data('Grumpy', grumpy) self.assertEqual(events, [{'added': ['Grumpy']}]) # While we're here, check that get_data works as advertised. grumpy_out = plot_data.get_data('Grumpy') self.assertIs(grumpy_out, grumpy) with self.monitor_events(plot_data) as events: plot_data.set_data('Grumpy', grumpy_too) self.assertEqual(events, [{'changed': ['Grumpy']}]) with self.monitor_events(plot_data) as events: plot_data.del_data('Grumpy') self.assertEqual(events, [{'removed': ['Grumpy']}]) if __name__ == '__main__': import nose nose.run() chaco-4.5.0/chaco/tests/arraydatasource_test_case.py0000644000076600000240000000476112426462410023402 0ustar jrocherstaff00000000000000""" Test of basic dataseries behavior. """ import unittest from numpy import arange, array, allclose, empty, isnan, nan import numpy as np from chaco.api import ArrayDataSource, PointDataSource class ArrayDataTestCase(unittest.TestCase): def test_basic_set_get(self): myarray = arange(10) sd = ArrayDataSource(myarray) self.assertTrue(allclose(myarray, sd._data)) self.assert_(sd.value_dimension == "scalar") return def test_bounds(self): # ascending myarray = arange(10) sd = ArrayDataSource(myarray, sort_order="ascending") bounds = sd.get_bounds() self.assert_(bounds == (0,9)) # descending myarray = arange(10)[::-1] sd = ArrayDataSource(myarray, sort_order="descending") bounds = sd.get_bounds() self.assert_(bounds == (0,9)) # no order myarray = array([12,3,0,9,2,18,3]) sd = ArrayDataSource(myarray, sort_order="none") bounds = sd.get_bounds() self.assert_(bounds == (0,18)) return def test_data_size(self): # We know that ScalarData always returns the exact length of its data myarray = arange(913) sd = ArrayDataSource(myarray) self.assert_(len(myarray) == sd.get_size()) return def test_bounds_all_nans(self): myarray = empty(10) myarray[:] = nan sd = ArrayDataSource(myarray) bounds = sd.get_bounds() self.assertTrue(isnan(bounds[0])) self.assertTrue(isnan(bounds[1])) def test_bounds_non_numeric(self): myarray = np.array([u'abc', u'foo', u'bar', u'def'], dtype=unicode) sd = ArrayDataSource(myarray) bounds = sd.get_bounds() self.assertEqual(bounds, (u'abc', u'def')) class PointDataTestCase(unittest.TestCase): # Since PointData is mostly the same as ScalarData, the key things to # test are functionality that use _compute_bounds() and reverse_map(). def create_array(self): return array(zip(range(10), range(0, 100, 10))) def test_basic_set_get(self): myarray = self.create_array() pd = PointDataSource(myarray) self.assertTrue(allclose(myarray,pd._data)) self.assert_(pd.value_dimension == "point") return def test_bounds(self): myarray = self.create_array() pd = PointDataSource(myarray) self.assertEqual(pd.get_bounds(),((0,0), (9,90))) return if __name__ == '__main__': import nose nose.run() chaco-4.5.0/chaco/tests/base_utils_test_case.py0000644000076600000240000001226212426452312022336 0ustar jrocherstaff00000000000000""" Unit tests for utility functions in chaco.base """ import unittest from math import sqrt from numpy import arange, array from numpy.testing import assert_equal, assert_almost_equal from chaco.api import bin_search, find_runs, reverse_map_1d, point_line_distance class BinSearchTestCase(unittest.TestCase): def test_ascending_data(self): ary = arange(10.0) # inside bounds self.assert_(bin_search(ary, 0.0, 1) == 0) self.assert_(bin_search(ary, 5.0, 1) == 5) self.assert_(bin_search(ary, 9.0, 1) == 9) # out of bounds self.assert_(bin_search(ary, 10.0, 1) == -1) self.assert_(bin_search(ary, -1.0, 1) == -1) self.assert_(bin_search(ary, 9.00001, 1) == -1) self.assert_(bin_search(ary, -0.00001, 1) == -1) # rounding self.assert_(bin_search(ary, 5.1, 1) == 5) self.assert_(bin_search(ary, 4.9, 1) == 4) return def test_descending_data(self): ary = arange(10.0, 0.0, -1.0) # inside bounds self.assert_(bin_search(ary, 10.0, -1) == 0) self.assert_(bin_search(ary, 5.0, -1) == 5) self.assert_(bin_search(ary, 1.0, -1) == 9) # out of bounds self.assert_(bin_search(ary, 10.1, -1) == -1) self.assert_(bin_search(ary, 0.9, -1) == -1) # rounding self.assert_(bin_search(ary, 5.1, -1) == 4) self.assert_(bin_search(ary, 4.9, -1) == 5) return class ReverseMap1DTestCase(unittest.TestCase): def test_ascending(self): ary = arange(10.0) rmap = lambda x: reverse_map_1d(ary, x, 'ascending') # inside bounds self.assert_(rmap(0.0) == 0) self.assert_(rmap(5.0) == 5) self.assert_(rmap(9.0) == 9) # out of bounds self.assertRaises(IndexError, rmap, 10.0) self.assertRaises(IndexError, rmap, -1.0) # rounding self.assert_(rmap(3.4) == 3) self.assert_(rmap(3.5) == 3) self.assert_(rmap(3.6) == 4) return def test_ascending_floor(self): ary = arange(10.0) rmap = lambda x: reverse_map_1d(ary, x, 'ascending', floor_only=True) # test rounding self.assert_(rmap(3.4) == 3) self.assert_(rmap(3.5) == 3) self.assert_(rmap(3.6) == 3) return def test_descending(self): ary = arange(10.0, 0.0, -1.0) rmap = lambda x: reverse_map_1d(ary, x, 'descending') # inside bounds self.assert_(rmap(10.0) == 0) self.assert_(rmap(5.0) == 5) self.assert_(rmap(1.0) == 9) # out of bounds self.assertRaises(IndexError, rmap, 0.0) self.assertRaises(IndexError, rmap, 11.0) # rounding self.assert_(rmap(8.6) == 1) self.assert_(rmap(8.5) == 1) self.assert_(rmap(8.4) == 2) return def test_descending_floor(self): ary = arange(10.0, 0.0, -1.0) rmap = lambda x: reverse_map_1d(ary, x, 'descending', floor_only=True) # test rounding self.assert_(rmap(8.6) == 1) self.assert_(rmap(8.5) == 1) self.assert_(rmap(8.4) == 1) return class FindRunsTestCase(unittest.TestCase): def test_find_runs_middle(self): x = array([0,8,7,8,9,2,3,4,10]) assert_equal(find_runs(x) , [[0], [8], [7,8,9], [2,3,4], [10]]) def test_find_runs_start(self): x = array([3,4,5,12,9,17]) assert_equal(find_runs(x) , [[3,4,5],[12],[9],[17]]) def test_find_runs_end(self): x = array([18,23,24,25]) assert_equal(find_runs(x) , [[18],[23,24,25]]) def test_find_runs_offset(self): # because of the nature of the find_runs algorithm, there may be # fencepost errors with runs that start at x[1] or x[-2] x = array([10,12,13,14,28,16]) assert_equal(find_runs(x) , [[10],[12,13,14],[28],[16]]) x = array([10,15,16,17,34]) assert_equal(find_runs(x) , [[10],[15,16,17],[34]]) def test_find_runs_none(self): x = array([]) assert_equal(find_runs(x) , []) x = array([12,15,27]) assert_equal(find_runs(x) , [[12],[15],[27]]) def test_find_runs_descending(self): x = array([30,41,40,39,38,37,12]) assert_equal(find_runs(x, order='descending') , \ [[30], [41,40,39,38,37], [12]]) class PointLineDistanceTestCase(unittest.TestCase): def test_horizontal_line(self): p1 = (10.0, 10.0) p2 = (60.0, 10.0) test = (35.0, 30.0) dist = point_line_distance(test, p1, p2) assert_equal(dist, 20.0) def test_vertical_line(self): p1 = (10.0, 10.0) p2 = (10.0, 60.0) test = (30.0, 35.0) dist = point_line_distance(test, p1, p2) assert_equal(dist, 20.0) def test_diag_lines(self): p1 = (0.0, 0.0) p2 = (10.0, 10.0) test = (0.0, 5.0) dist = point_line_distance(test, p1, p2) assert_almost_equal(dist, 2.5 * sqrt(2.0)) def test_point_on_line(self): p1 = (-5.0, 5.0) p2 = (10.0, -10.0) test = (3.0, -3.0) dist = point_line_distance(test, p1, p2) assert_almost_equal(dist, 0.0) if __name__ == '__main__': import nose nose.run() chaco-4.5.0/chaco/tests/border_test_case.py0000644000076600000240000000233612426452312021462 0ustar jrocherstaff00000000000000""" Needed Tests Component.draw_border() tests -------------------- DONE *. draw_border output should match a similar draw_rect output """ import nose import unittest from numpy import array, alltrue, ravel # Chaco imports from chaco.api import Plot, PlotGraphicsContext class DrawBorderTestCase(unittest.TestCase): def assertRavelEqual(self, x, y): self.assert_(alltrue(ravel(x) == ravel(y)), "\n%s\n !=\n%s" % (x, y)) def test_draw_border_simple(self): """ Borders should have the correct height and width. """ size = (5,5) container = Plot(padding=1, border_visible=True) container.outer_bounds = list(size) gc = PlotGraphicsContext(size) gc.render_component(container) desired = array(((255, 255, 255, 255, 255, 255), (255, 0, 0, 0, 0, 255), (255, 0, 255, 255, 0, 255), (255, 0, 255, 255, 0, 255), (255, 0, 0, 0, 0, 255), (255, 255, 255, 255, 255, 255))) actual = gc.bmp_array[:,:,0] self.assertRavelEqual(actual, desired) if __name__ == "__main__": unittest.main() chaco-4.5.0/chaco/tests/colormapper_test_case.py0000644000076600000240000001250212426452312022524 0ustar jrocherstaff00000000000000import unittest from numpy import allclose, array, ravel from chaco.api import ArrayDataSource, ColorMapper, DataRange1D class LinearSegmentedColormapTestCase(unittest.TestCase): def setUp(self): """ Set up called before each test case. """ _gray_data = {'red': [(0., 0, 0), (1., 1.0, 1.0)], 'green': [(0., 0, 0), (1., 1.0, 1.0)], 'blue': [(0., 0, 0), (1., 1.0, 1.0)]} self.colormap = ColorMapper.from_segment_map(_gray_data) self.colormap.range = DataRange1D() def test_simple_map(self): a = ArrayDataSource(array([0.0, 0.5, 1.0])) self.colormap.range.add(a) b = self.colormap.map_screen(a.get_data()) self.colormap.range.remove(a) expected = array([0.0, 0.5, 1.0]) close = allclose(ravel(b[:,:1]), expected, atol=0.02) self.assert_(close, "Simple map failed. Expected %s. Got %s" % (expected, b[:,:1])) return def test_change_min_max(self): """ Test that changing min_value and max_value does not break map. """ datarange = self.colormap.range # Perform a dummy mapping. a = ArrayDataSource(array([0.0, 0.5, 1.0])) datarange.add(a) b = self.colormap.map_screen(a.get_data()) datarange.remove(a) # Update the min_value. datarange.low = -1.0 # Test that the map still works. a = ArrayDataSource(array([-1.0, 0.0, 1.0])) datarange.add(a) b = self.colormap.map_screen(a.get_data()) datarange.remove(a) expected = array([0.0, 0.5, 1.0]) close = allclose(ravel(b[:,:1]), expected, atol=0.02) self.assert_(close, "Changing min value broke map. Expected %s. Got %s" % (expected, b[:,:1])) # Update the max_value. datarange.high = 0.0 # Test that the map still works. a = ArrayDataSource(array([-1.0, -0.5, 0.0])) datarange.add(a) b = self.colormap.map_screen(a.get_data()) datarange.remove(a) expected = array([0.0, 0.5, 1.0]) close = allclose(ravel(b[:,:1]), expected, atol=0.02) self.assert_(close, "Changing min value broke map. Expected %s. Got %s" % (expected, b[:,:1])) return def test_array_factory(self): """ Test that the array factory creates valid colormap. """ colors = array([[0.0,0.0,0.0], [1.0,1.0,1.0]]) cm = ColorMapper.from_palette_array(colors) cm.range = DataRange1D() ar = ArrayDataSource(array([0.0, 0.5, 1.0])) cm.range.add(ar) b = cm.map_screen(ar.get_data()) cm.range.remove(ar) expected = array([0.0, 0.5, 1.0]) self.assertTrue(allclose(ravel(b[:,:1]), expected, atol=0.02), "Array factory failed. Expected %s. Got %s" % (expected, b[:,:1])) return def test_alpha_palette(self): """ Create a colormap with a varying alpha channel from a palette array. """ cm = ColorMapper.from_palette_array([[0.0,0.0,0.0,0.5],[1.0,1.0,1.0,1.0]]) sd = {'alpha': [(0.0, 0.5, 0.5), (1.0, 1.0, 1.0)], 'blue': [(0.0, 0.0, 0.0), (1.0, 1.0, 1.0)], 'green': [(0.0, 0.0, 0.0), (1.0, 1.0, 1.0)], 'red': [(0.0, 0.0, 0.0), (1.0, 1.0, 1.0)]} assert cm._segmentdata == sd def test_alpha_segment_data(self): """ Create a colormap with a varying alpha channel from segment data. """ sd = {'alpha': [(0.0, 0.5, 0.5), (1.0, 1.0, 1.0)], 'blue': [(0.0, 0.0, 0.0), (1.0, 1.0, 1.0)], 'green': [(0.0, 0.0, 0.0), (1.0, 1.0, 1.0)], 'red': [(0.0, 0.0, 0.0), (1.0, 1.0, 1.0)]} cm = ColorMapper.from_segment_map(sd) assert cm._segmentdata == sd def test_no_alpha(self): """ Check that the defaults when no alpha is specified are correct. """ sd = {'alpha': [(0.0, 1.0, 1.0), (1.0, 1.0, 1.0)], 'blue': [(0.0, 0.0, 0.0), (1.0, 1.0, 1.0)], 'green': [(0.0, 0.0, 0.0), (1.0, 1.0, 1.0)], 'red': [(0.0, 0.0, 0.0), (1.0, 1.0, 1.0)]} assert self.colormap._segmentdata == sd ## def test_no_interpolation(self): ## grayscale_colors = array([[0.0,0.0,0.0,1.0], [1.0, 1.0, 1.0, 1.0]]) ## grayscale_bins = array([0.0, 1.0]) ## grayscale_steps = array([1]) ## colormap = LinearSegmentedColormap( ## grayscale_colors, grayscale_bins, grayscale_steps ## ) ## a = array([0.0, 0.25, 0.75, 1.0]) ## b = colormap.map_array(a) ## result = ravel(b[:,:1]) ## expected = array([0.0, 0.0, 1.0, 1.0]) ## close = allclose(result, expected, atol=0.02) ## self.assert_(close, ## "Map with no interpolation broken. Expected %s. Got %s" % (expected, result)) ## def test_value_bands(self): ## grayscale_colors = array([[0.0,0.0,0.0,1.0], [1.0, 1.0, 1.0, 1.0]]) ## grayscale_bins = array([0.0, 1.0]) ## grayscale_steps = array([1]) ## colormap = LinearSegmentedColormap( ## grayscale_colors, grayscale_bins, grayscale_steps ## ) ## colormap._recalculate() ## print '**************', colormap._color_bands, colormap._value_bands if __name__ == '__main__': import nose nose.run() chaco-4.5.0/chaco/tests/component_tests.py0000644000076600000240000000310312426452312021370 0ustar jrocherstaff00000000000000from enable.api import Component def test_padding_init(): """ Make sure that padding traits passed to the constructor get set in the correct order. """ c = Component() assert c.padding_top == 0 assert c.padding_bottom == 0 assert c.padding_left == 0 assert c.padding_right == 0 c = Component(padding=50) assert c.padding_top == 50 assert c.padding_bottom == 50 assert c.padding_left == 50 assert c.padding_right == 50 c = Component(padding=50, padding_top=15) assert c.padding_top == 15 assert c.padding_bottom == 50 assert c.padding_left == 50 assert c.padding_right == 50 c = Component(padding=50, padding_bottom=15) assert c.padding_top == 50 assert c.padding_bottom == 15 assert c.padding_left == 50 assert c.padding_right == 50 c = Component(padding=50, padding_left=15) assert c.padding_top == 50 assert c.padding_bottom == 50 assert c.padding_left == 15 assert c.padding_right == 50 c = Component(padding=50, padding_right=15) assert c.padding_top == 50 assert c.padding_bottom == 50 assert c.padding_left == 50 assert c.padding_right == 15 def test_padding_trait_default(): class PaddedComponent(Component): padding_top = 50 c = PaddedComponent() assert c.padding_top == 50 assert c.padding_bottom == 0 assert c.padding_left == 0 assert c.padding_right == 0 c = PaddedComponent(padding_left=15) assert c.padding_top == 50 assert c.padding_bottom == 0 assert c.padding_left == 15 assert c.padding_right == 0 chaco-4.5.0/chaco/tests/data_view_test_case.py0000644000076600000240000000235212426452312022146 0ustar jrocherstaff00000000000000 import unittest from chaco.api import DataRange2D, DataView, GridDataSource class DataViewTestCase(unittest.TestCase): def test_empty(self): dv = DataView() self.assert_(dv.orientation=="h") self.assert_(dv.index_scale=="linear") self.assert_(dv.bgcolor=="white") self.assert_(dv.overlay_border==True) self.assert_(dv.range2d.x_range==dv.index_range) self.assert_(dv.range2d.y_range==dv.value_range) def test_orientation(self): dv = DataView() x_mapper_start = dv.x_mapper y_mapper_start = dv.y_mapper dv.orientation = "v" self.assert_(dv.x_mapper is y_mapper_start) self.assert_(dv.y_mapper is x_mapper_start) def test_range2d_changed(self): dv = DataView() ds = GridDataSource() dv.range2d.add(ds) old_range = dv.range2d r = DataRange2D() self.assert_(dv.range2d.sources==[ds]) dv.range2d = r self.assert_(dv.range2d.sources==[ds]) self.assert_(old_range.sources==[]) self.assert_(dv.range2d.x_range is dv.index_mapper.range) self.assert_(dv.range2d.y_range is dv.value_mapper.range) if __name__ == '__main__': import nose nose.run() chaco-4.5.0/chaco/tests/datarange_1d_test_case.py0000644000076600000240000002460612426452312022523 0ustar jrocherstaff00000000000000 import unittest from numpy import arange, array, zeros, inf from numpy.testing import assert_equal from traits.api import HasTraits, Instance, Bool, on_trait_change from chaco.api import DataRange1D, ArrayDataSource class Foo(HasTraits): """ This class is used to test the firing of the `updated` event of DataRange1D. """ range = Instance(DataRange1D) range_updated = Bool(False) @on_trait_change('range.updated') def range_changed(self): self.range_updated = True class DataRangeTestCase(unittest.TestCase): def test_empty_range(self): r = DataRange1D() self.assertEqual(r.low, -inf) self.assertEqual(r.high, inf) self.assertEqual(r.low_setting, "auto") self.assertEqual(r.high_setting, "auto") r.low = 5.0 r.high = 10.0 self.assertEqual(r.low_setting, 5.0) self.assertEqual(r.high_setting, 10.0) self.assertEqual(r.low, 5.0) self.assertEqual(r.high, 10.0) return def test_set_bounds1(self): """Change both low and high with set_bounds().""" foo = Foo(range=DataRange1D(low=0.0, high=1.0)) # Paranoid check first (not the main point of this test): self.assertEqual(foo.range.low, 0.0) self.assertEqual(foo.range.high, 1.0) # Now reset foo's range_updated flag and set the bounds with set_bounds(). foo.range_updated = False foo.range.set_bounds(-1.0, 2.0) # Verify the values. self.assertEqual(foo.range.low, -1.0) self.assertEqual(foo.range.high, 2.0) # Verify that the `updated` event fired. self.assert_(foo.range_updated) def test_set_bounds2(self): """Change only the high value with set_bounds().""" foo = Foo(range=DataRange1D(low=0.0, high=1.0)) # Paranoid check first (not the main point of this test): self.assertEqual(foo.range.low, 0.0) self.assertEqual(foo.range.high, 1.0) # Now reset foo's range_updated flag and set the bounds with set_bounds(). foo.range_updated = False foo.range.set_bounds(0.0, 2.0) # Verify the values. self.assertEqual(foo.range.low, 0.0) self.assertEqual(foo.range.high, 2.0) # Verify that the `updated` event fired. self.assert_(foo.range_updated) def test_set_bounds3(self): """Change only the low value with set_bounds().""" foo = Foo(range=DataRange1D(low=0.0, high=1.0)) # Paranoid check first (not the main point of this test): self.assertEqual(foo.range.low, 0.0) self.assertEqual(foo.range.high, 1.0) # Now reset foo's range_updated flag and set the bounds with set_bounds(). foo.range_updated = False foo.range.set_bounds(0.5, 1.0) # Verify the values. self.assertEqual(foo.range.low, 0.5) self.assertEqual(foo.range.high, 1.0) # Verify that the `updated` event fired. self.assert_(foo.range_updated) def test_set_bounds4(self): """Set set_bounds() with high='track'.""" foo = Foo(range=DataRange1D(tracking_amount=1.0)) foo.range.low_setting = 0.0 foo.range.high_setting = 'track' # Paranoid check first (not the main point of this test): self.assertEqual(foo.range.low, 0.0) self.assertEqual(foo.range.high, 1.0) # Now reset foo's range_updated flag and set the bounds with set_bounds(). foo.range_updated = False foo.range.set_bounds(100.0, 'track') print foo.range.low, foo.range.high # Verify the values. self.assertEqual(foo.range.low, 100.0) self.assertEqual(foo.range.high, 101.0) # Verify that the `updated` event fired. self.assert_(foo.range_updated) def test_set_bounds5(self): """Set set_bounds() with low='track'.""" foo = Foo(range=DataRange1D(tracking_amount=1.0)) foo.range.low_setting = 'track' foo.range.high_setting = 1.0 # Paranoid check first (not the main point of this test): self.assertEqual(foo.range.low, 0.0) self.assertEqual(foo.range.high, 1.0) # Now reset foo's range_updated flag and set the bounds with set_bounds(). foo.range_updated = False foo.range.set_bounds('track', 100.0) # Verify the values. self.assertEqual(foo.range.low, 99.0) self.assertEqual(foo.range.high, 100.0) # Verify that the `updated` event fired. self.assert_(foo.range_updated) def test_set_tracking_amount(self): """Test setting the tracking amount using the set_tracking_amount() method.""" foo = Foo(range=DataRange1D(tracking_amount=1.0)) foo.range.low_setting = 'track' foo.range.high_setting = 1.0 # Paranoid check first (not the main point of this test): self.assertEqual(foo.range.low, 0.0) self.assertEqual(foo.range.high, 1.0) # Now reset foo's range_updated flag and change the tracking amount. foo.range_updated = False foo.range.set_tracking_amount(2.0) # Verify the values. self.assertEqual(foo.range.low, -1.0) self.assertEqual(foo.range.high, 1.0) # Verify that the `updated` event fired. self.assert_(foo.range_updated) def test_scale_tracking_amount(self): """Test setting the tracking amount using the scale_tracking_amount() method.""" foo = Foo(range=DataRange1D(tracking_amount=1.0)) foo.range.low_setting = 'track' foo.range.high_setting = 1.0 # Paranoid check first (not the main point of this test): self.assertEqual(foo.range.low, 0.0) self.assertEqual(foo.range.high, 1.0) # Now reset foo's range_updated flag and change the tracking amount. foo.range_updated = False foo.range.scale_tracking_amount(0.5) # Verify the values. self.assertEqual(foo.range.low, 0.5) self.assertEqual(foo.range.high, 1.0) # Verify that the `updated` event fired. self.assert_(foo.range_updated) def test_single_source(self): r = DataRange1D() ary = arange(10.0) ds = ArrayDataSource(ary) r.sources.append(ds) self.assertEqual(r.low, 0.0) self.assertEqual(r.high, 9.0) r.low = 3.0 r.high = 6.0 self.assertEqual(r.low_setting, 3.0) self.assertEqual(r.high_setting, 6.0) self.assertEqual(r.low, 3.0) self.assertEqual(r.high, 6.0) r.refresh() self.assertEqual(r.low_setting, 3.0) self.assertEqual(r.high_setting, 6.0) self.assertEqual(r.low, 3.0) self.assertEqual(r.high, 6.0) r.low = "auto" self.assertEqual(r.low_setting, "auto") self.assertEqual(r.low, 0.0) return def test_constant_value(self): r = DataRange1D() ary = array([3.14]) ds = ArrayDataSource(ary) r.add(ds) # A constant value > 1.0, by default, gets a range that brackets # it to the nearest power of ten above and below self.assertEqual(r.low, 1.0) self.assertEqual(r.high, 10.0) r.remove(ds) ds = ArrayDataSource(array([31.4])) r.add(ds) self.assertEqual(r.low, 10.0) self.assertEqual(r.high, 100.0) r.remove(ds) ds = ArrayDataSource(array([0.125])) r.add(ds) self.assertEqual(r.low, 0.0) self.assertEqual(r.high, 0.25) r.remove(ds) ds = ArrayDataSource(array([-0.125])) r.add(ds) self.assertEqual(r.low, -0.25) self.assertEqual(r.high, 0.0) return def test_multi_source(self): ds1 = ArrayDataSource(array([3, 4, 5, 6, 7])) ds2 = ArrayDataSource(array([5, 10, 15, 20])) r = DataRange1D(ds1, ds2) self.assertEqual(r.low, 3.0) self.assertEqual(r.high, 20.0) return def test_clip_data(self): r = DataRange1D(low=2.0, high=10.0) ary = array([1, 3, 4, 9.8, 10.2, 12]) assert_equal(r.clip_data(ary) , array([3.0,4.0,9.8])) r = DataRange1D(low=10, high=20) ary = array([5, 10, 15, 20, 25, 30]) assert_equal(r.clip_data(ary) , array([10, 15, 20])) assert_equal(r.clip_data(ary[::-1]) , array([20, 15, 10])) r = DataRange1D(low=2.0, high=2.5) assert_equal(len(r.clip_data(ary)) , 0) return def test_mask_data(self): r = DataRange1D(low=2.0, high=10.0) ary = array([1, 3, 4, 9.8, 10.2, 12]) assert_equal(r.mask_data(ary) , array([0,1,1,1,0,0], 'b')) r = DataRange1D(low=10, high=20) ary = array([5, 10, 15, 20, 25, 30]) target_mask = array([0,1,1,1,0,0], 'b') assert_equal(r.mask_data(ary) , target_mask) assert_equal(r.mask_data(ary[::-1]) , target_mask[::-1]) r = DataRange1D(low=2.0, high=2.5) assert_equal(r.mask_data(ary) , zeros(len(ary))) return def test_bound_data(self): r = DataRange1D(low=2.9, high=6.1) ary = arange(10) assert_equal(r.bound_data(ary) , (3,6)) # test non-monotonic data ary = array([-5,-4,-7,-8,-2,1,2,3,4,5,4,3,8,9,10,9,8]) bounds = r.bound_data(ary) assert_equal(bounds , (7,11)) return def test_custom_bounds_func(self): def custom_func(low, high, margin, tight_bounds): assert_equal(low, 0.0) assert_equal(high, 9.0) assert_equal(tight_bounds, False) assert_equal(margin, 1.0) return -999., 999. r = DataRange1D(tight_bounds=False, margin=1.0, bounds_func=custom_func) ary = arange(10.0) ds = ArrayDataSource(ary) r.sources.append(ds) assert_equal(r.low, -999.) assert_equal(r.high, 999.) def test_inf_in_source(self): r = DataRange1D() ary1 = array([1.0, inf]) ds1 = ArrayDataSource(ary1) r.sources.append(ds1) self.assertEqual(r.low, 1.0) self.assertEqual(r.high, inf) data = array([-100.0, 0.0, 100.0]) assert_equal(r.clip_data(data) , array([100.0])) r = DataRange1D() ary2 = array([-inf, 1.0]) ds2 = ArrayDataSource(ary2) r.sources.append(ds2) self.assertEqual(r.low, -inf) self.assertEqual(r.high, 1.0) r.sources.append(ds1) self.assertEqual(r.low, -inf) self.assertEqual(r.high, inf) if __name__ == '__main__': import nose nose.run() chaco-4.5.0/chaco/tests/datarange_2d_test_case.py0000644000076600000240000001571712426462254022535 0ustar jrocherstaff00000000000000 import unittest from numpy import alltrue, arange, array, ravel, transpose, zeros, inf, isinf from numpy.testing import assert_equal, assert_ from chaco.api import DataRange2D, GridDataSource, PointDataSource class DataRange2DTestCase(unittest.TestCase): def test_empty_range(self): r = DataRange2D() assert_ary_(r.low,array([-inf,-inf])) assert_ary_(r.high,array([inf,inf])) self.assert_(r.low_setting == ('auto','auto')) self.assert_(r.high_setting == ('auto', 'auto')) r.low = array([5.0,5.0]) r.high = array([10.0,10.0]) assert_ary_(r.low_setting, array([5.0,5.0])) assert_ary_(r.high_setting, array([10.0,10.0])) assert_ary_(r.low,array([5.0,5.0])) assert_ary_(r.high, array([10.0,10.0])) return def test_single_source(self): r = DataRange2D() x = arange(10.) y = arange(0.,100.,10.) ds = PointDataSource(transpose(array([x,y])), sort_order="none") r.add(ds) assert_ary_(r.low, array([0.,0.])) assert_ary_(r.high, array([9.0,90.0])) r.low = [3.0,30.0] r.high = [6.0,60.0] assert_ary_(r.low_setting, array([3.0,30.0])) assert_ary_(r.high_setting, array([6.0,60.0])) assert_ary_(r.low, array([3.0,30.0])) assert_ary_(r.high, array([6.0,60.0])) r.refresh() assert_ary_(r.low_setting, array([3.0,30.0])) assert_ary_(r.high_setting, array([6.0,60.0])) assert_ary_(r.low, array([3.0,30.0])) assert_ary_(r.high, array([6.0,60.0])) r.low = ('auto', 'auto') self.assert_(r.low_setting == ('auto', 'auto')) assert_ary_(r.low, array([0.0,0.0])) return def test_constant_values(self): r = DataRange2D() ds = PointDataSource(array([[5.0,5.0]]), sort_order="none") r.add(ds) # A constant value > 1.0, by default, gets a range that brackets # it to the nearest power of ten above and below assert_ary_(r.low, array([1.0,1.0])) assert_ary_(r.high, array([10.0,10.0])) r.remove(ds) ds = PointDataSource(array([[31.4,9.7]])) r.add(ds) assert_ary_(r.low, array([10.0,1.0])) assert_ary_(r.high, array([100.0,10.0])) r.remove(ds) ds = PointDataSource(array([[0.125,0.125]])) r.add(ds) assert_ary_(r.low, array([0.0, 0.0])) assert_ary_(r.high, array([0.25, 0.25])) r.remove(ds) ds = PointDataSource(array([[-0.125, -0.125]])) r.add(ds) assert_ary_(r.low, array([-0.25, -0.25])) assert_ary_(r.high, array([0.0, 0.0])) return def test_multi_source(self): x = arange(10.) y = arange(0.,100.,10.) foo = transpose(array([x,y])) bar = transpose(array([y,x])) ds1 = PointDataSource(foo) ds2 = PointDataSource(bar) r = DataRange2D(ds1, ds2) assert_ary_(r.low, [0.0,0.0]) assert_ary_(r.high, [90.,90.]) return def test_grid_source(self): test_xd1 = array([1,2,3]) test_yd1 = array([1.5, 0.5, -0.5, -1.5]) test_sort_order1 = ('ascending', 'descending') test_xd2 = array([0,50,100]) test_yd2 = array([0.5, 0.75, 1]) ds1 = GridDataSource(xdata=test_xd1, ydata=test_yd1, sort_order=test_sort_order1) ds2 = GridDataSource(xdata=test_xd2, ydata=test_yd2) r = DataRange2D() r.add(ds1) assert_ary_(r.low, array([1,-1.5])) assert_ary_(r.high, array([3,1.5])) r.add(ds2) assert_ary_(r.low, array([0.0,-1.5])) assert_ary_(r.high, array([100,1.5])) r.remove(ds1) assert_ary_(r.low, array([0,0.5])) assert_ary_(r.high, array([100,1])) r.remove(ds2) assert_ary_(r.low, array([-inf,-inf])) assert_ary_(r.high, array([inf,inf])) def test_set_bounds(self): test_xd = array([-10,10]) test_yd = array([-10,10]) ds = GridDataSource(xdata=test_xd, ydata=test_yd) r = DataRange2D() r.set_bounds((-1,-2), (3,4)) assert_ary_(r.low, array([-1,-2])) assert_ary_(r.high, array([3,4])) r.add(ds) assert_ary_(r.low, array([-1,-2])) r.low_setting = ('auto','auto') assert_ary_(r.low, array([-10,-10])) assert_ary_(r.high, array([3,4])) r.high_setting = ('auto','auto') assert_ary_(r.low, array([-10,-10])) assert_ary_(r.high, array([10,10])) r.set_bounds((-100,-100), (100,100)) assert_ary_(r.low, array([-100,-100])) assert_ary_(r.high, array([100,100])) def test_reset_bounds(self): r = DataRange2D() low = (13, 42) high = (1337, 9001) r.set_bounds(low, high) self.assertEqual(r.low_setting, low) self.assertEqual(r.high_setting, high) r.reset() self.assertEqual(r.low_setting, ('auto', 'auto')) self.assertEqual(r.high_setting, ('auto', 'auto')) self.assertEqual(r.x_range.low_setting, 'auto') self.assertEqual(r.y_range.low_setting, 'auto') self.assertEqual(r.x_range.high_setting, 'auto') self.assertEqual(r.y_range.high_setting, 'auto') def test_clip_data(self): r = DataRange2D(low=[2.0,5.0], high=[10.0,8.0]) x= arange(10.0) y= arange(0.,20.,2.) ary= transpose(array([x,y])) assert_equal(r.clip_data(ary) , array([[3.,6.],[4.,8.]])) r = DataRange2D(low=[10.,10.], high=[20.,20.]) x= arange(10.0,30.,2.) y= arange(0.,40.,4.) ary = transpose(array([x,y])) assert_equal(r.clip_data(ary) , array([[16.,12.],[18.,16.],[20.,20.]])) assert_equal(r.clip_data(ary[::-1]) , array([[20,20], [18,16], [16,12]])) return def test_mask_data(self): r = DataRange2D(low=[2.0,5.0], high=[10.0,18.0]) x = array([1, 3, 4, 9.8, 10.2, 12]) y = array([5, 3, 7, 12, 18, 6]) ary = transpose(array([x,y])) assert_equal(r.mask_data(ary) , array([0,0,1,1,0,0], 'b')) r = DataRange2D(low=[10.,15.], high=[20.,25.]) x = array([5, 10, 15, 20, 25, 30]) y = array([5, 10, 15, 20, 25, 30]) ary = transpose(array([x,y])) target_mask = array([0,0,1,1,0,0], 'b') assert_equal(r.mask_data(ary) , target_mask) assert_equal(r.mask_data(ary[::-1]) , target_mask[::-1]) r = DataRange2D(low=[2.0,5.0], high=[2.5,9.0]) assert_equal(r.mask_data(ary) , zeros(len(ary))) return def assert_close_(desired,actual): diff_allowed = 1e-5 diff = abs(ravel(actual) - ravel(desired)) for d in diff: if not isinf(d): assert_(alltrue(d <= diff_allowed)) return def assert_ary_(desired, actual): if (desired == 'auto'): assert_equal(actual, 'auto') for d in range(len(desired)): assert_equal(desired[d], actual[d]) return if __name__ == '__main__': import nose nose.run() chaco-4.5.0/chaco/tests/default_colormaps_test_case.py0000644000076600000240000000415212426462254023714 0ustar jrocherstaff00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2014, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # Thanks for using Enthought open source! # #------------------------------------------------------------------------------ import unittest import numpy as np from numpy.testing import assert_array_equal from chaco.api import DataRange1D from .. import default_colormaps class DefaultColormapsTestCase(unittest.TestCase): def test_default_colormaps_smoke(self): # Runs some data through each of the default colormaps and do basic # sanity checks. x = np.linspace(-1.5, 2.0, 8) datarange = DataRange1D(low_setting=-1.0, high_setting=1.5) for cmap_func in default_colormaps.color_map_functions: cmapper = cmap_func(datarange) rgba = cmapper.map_screen(x) self.assertEqual(rgba.shape, (8, 4)) self.assertTrue(np.isfinite(rgba).all()) self.assertTrue((rgba >= 0.0).all()) self.assertTrue((rgba <= 1.0).all()) # No transparency for any of the defaults. assert_array_equal(rgba[:, -1], np.ones(8)) assert_array_equal(rgba[0], rgba[1]) assert_array_equal(rgba[-2], rgba[-1]) r_cmapper = default_colormaps.reverse(cmap_func)(datarange) r_rgba = r_cmapper.map_screen(x) assert_array_equal(r_rgba, rgba[::-1]) c_cmapper = default_colormaps.center(cmap_func)(datarange) self.assertEqual(c_cmapper.range.low, -1.5) self.assertEqual(c_cmapper.range.high, 1.5) f_cmapper = default_colormaps.fix(cmap_func, (0.0, 1.0))(datarange) self.assertEqual(f_cmapper.range.low, 0.0) self.assertEqual(f_cmapper.range.high, 1.0) chaco-4.5.0/chaco/tests/grid_data_source_test_case.py0000644000076600000240000000455212426452312023505 0ustar jrocherstaff00000000000000 import unittest from numpy import alltrue, array, ravel, isinf from chaco.api import GridDataSource class GridDataSourceTestCase(unittest.TestCase): def test_empty(self): ds = GridDataSource() self.assert_(ds.sort_order == ('none', 'none')) self.assert_(ds.index_dimension == 'image') self.assert_(ds.value_dimension == 'scalar') self.assert_(ds.metadata == {"selections":[], "annotations":[]}) xdata, ydata = ds.get_data() assert_ary_(xdata.get_data(), array([])) assert_ary_(ydata.get_data(), array([])) self.assert_(ds.get_bounds() == ((0,0),(0,0))) def test_init(self): test_xd = array([1,2,3]) test_yd = array([1.5, 0.5, -0.5, -1.5]) test_sort_order = ('ascending', 'descending') ds = GridDataSource(xdata=test_xd, ydata=test_yd, sort_order=test_sort_order) self.assert_(ds.sort_order == test_sort_order) xd, yd = ds.get_data() assert_ary_(xd.get_data(), test_xd) assert_ary_(yd.get_data(), test_yd) self.assert_(ds.get_bounds() == ((min(test_xd),min(test_yd)), (max(test_xd),max(test_yd)))) def test_set_data(self): ds = GridDataSource(xdata=array([1,2,3]), ydata=array([1.5, 0.5, -0.5, -1.5]), sort_order=('ascending', 'descending')) test_xd = array([0,2,4]) test_yd = array([0,1,2,3,4,5]) test_sort_order = ('none', 'none') ds.set_data(xdata=test_xd, ydata=test_yd, sort_order=('none', 'none')) self.assert_(ds.sort_order == test_sort_order) xd, yd = ds.get_data() assert_ary_(xd.get_data(), test_xd) assert_ary_(yd.get_data(), test_yd) self.assert_(ds.get_bounds() == ((min(test_xd),min(test_yd)), (max(test_xd),max(test_yd)))) def assert_close_(desired,actual): diff_allowed = 1e-5 diff = abs(ravel(actual) - ravel(desired)) for d in diff: if not isinf(d): assert alltrue(d <= diff_allowed) return def assert_ary_(desired, actual): if (desired == 'auto'): assert actual == 'auto' for d in range(len(desired)): assert desired[d] == actual[d] return if __name__ == '__main__': import nose nose.run() chaco-4.5.0/chaco/tests/grid_mapper_test_case.py0000644000076600000240000000342612426462425022505 0ustar jrocherstaff00000000000000 import unittest from numpy import array, transpose from numpy.testing import assert_equal from chaco.api import GridDataSource, DataRange2D, GridMapper class GridMapperTestCase(unittest.TestCase): def setUp(self): self.x_ary = array([5.0, 6.0, 7.0, 8.0, 9.0, 10.0]) self.y_ary = array([1.0, 1.0, 2.0, 2.0, 3.0, 3.0]) ds = GridDataSource(xdata=self.x_ary, ydata=self.y_ary) r = DataRange2D(ds) self.mapper = GridMapper(range=r) def test_basic(self): self.mapper.x_low_pos=50 self.mapper.x_high_pos=100 self.mapper.y_low_pos=0 self.mapper.y_high_pos=10 result = self.mapper.map_screen(transpose((self.x_ary, self.y_ary))) assert_equal(result, [(50,0), (60,0), (70,5), (80,5), (90,10), (100,10)]) def test_map_screen_scalar(self): self.mapper.x_low_pos=50 self.mapper.x_high_pos=100 self.mapper.y_low_pos=0 self.mapper.y_high_pos=10 result = self.mapper.map_screen(transpose((6.0, 1.0))) assert_equal(result, [[60, 0]]) def test_map_data(self): self.mapper.x_low_pos=50 self.mapper.x_high_pos=100 self.mapper.y_low_pos=0 self.mapper.y_high_pos=10 screen_ary = array([(50,0), (60,0), (70,5), (80,5), (90,10), (100,10)]) result = self.mapper.map_data(screen_ary) assert_equal(result, transpose((self.x_ary, self.y_ary))) def test_map_data_scalar(self): self.mapper.x_low_pos=50 self.mapper.x_high_pos=100 self.mapper.y_low_pos=0 self.mapper.y_high_pos=10 screen_ary = (60, 0) result = self.mapper.map_data(screen_ary) assert_equal(result, [[6.0, 1.0]]) if __name__ == '__main__': import nose nose.run() chaco-4.5.0/chaco/tests/hittest_test_case.py0000644000076600000240000000451412426452312021671 0ustar jrocherstaff00000000000000""" Test cases for the LinePlot's hittest() function """ import unittest from numpy import arange, array, linalg from chaco.api import (ArrayDataSource, ArrayPlotData, Plot, LinearMapper, DataRange1D) class HittestTestCase(unittest.TestCase): def make_plot(self, orientation): # make some data points x = arange(3) x = ArrayDataSource(x, sort_order="ascending") y = array([2,0,1]) # Plot the data pd = ArrayPlotData(x=x, y=y) plot = Plot(pd, orientation=orientation) line_plot = plot.plot(("x", "y"))[0] # Construct a fake screen space for the plots # otherwise would need to actually display the plots to get this index_mapper = LinearMapper(data_range=DataRange1D(low=0,high=2), high_pos=380, low_pos=20) value_mapper = LinearMapper(data_range=DataRange1D(low=0,high=2), high_pos=380, low_pos=20) plot.index_mapper = index_mapper plot.value_mapper = value_mapper line_plot.index_mapper = index_mapper line_plot.value_mapper = value_mapper return plot, line_plot def test_horizontal(self): plot, line_plot = self.make_plot("h") self._test_plot(plot, line_plot, point=[0.5,1]) self._test_plot(plot, line_plot, point=[1,0]) def test_vertical(self): plot, line_plot = self.make_plot("v") self._test_plot(plot, line_plot, point=[0.5,1]) self._test_plot(plot, line_plot, point=[1,0]) def _test_plot(self, plot, line_plot, point): threshold = 2 # In pixels screen_pt = plot.map_screen(point).flatten() result = line_plot.hittest(screen_pt, threshold=threshold) self.assertTrue(result is not None) # Check that the result is close by threshold in screenspace screen_result = plot.map_screen(result) self.assertTrue(linalg.norm(screen_pt - screen_result) < threshold) # check the return_distance = True case: x, y, d = line_plot.hittest(screen_pt, threshold=threshold, return_distance=True) self.assertEqual(x, result[0]) self.assertEqual(y, result[1]) self.assertTrue(d < threshold) if __name__ == '__main__': import nose nose.run() chaco-4.5.0/chaco/tests/instantiation_order_test_case.py0000644000076600000240000000263712426452312024270 0ustar jrocherstaff00000000000000""" Tests that various plot and data objects can be instantiated, assigned, and re-assigned in any order. """ import unittest from numpy import array from chaco.api import ArrayDataSource, DataRange1D, \ LinearMapper class DataPipelineTestCase(unittest.TestCase): def test_piecewise_construction(self): ary = array([1,2,3,4,5,6,7]) ds = ArrayDataSource() ds.set_data(ary) r = DataRange1D() r.add(ds) self.assert_(r.low_setting == "auto") self.assert_(r.high_setting == "auto") self.assert_(r.low == 1) self.assert_(r.high == 7) mapper = LinearMapper() mapper.range = r mapper.low_pos = 1.0 mapper.high_pos = 7.0 screen_pts = mapper.map_screen(array([1,3,7])) self.assert_(tuple(screen_pts) == (1.0, 3.0, 7.0)) return def test_reverse_construction(self): mapper = LinearMapper() r = DataRange1D() ds = ArrayDataSource() ary = array([1,2,3,4,5,6,7]) mapper.range = r mapper.low_pos = 1.0 mapper.high_pos = 7.0 r.add(ds) ds.set_data(ary) self.assert_(r.low == 1) self.assert_(r.high == 7) screen_pts = mapper.map_screen(array([1,3,7])) self.assert_(tuple(screen_pts) == (1.0, 3.0, 7.0)) return if __name__ == '__main__': import nose nose.run() chaco-4.5.0/chaco/tests/linearmapper_test_case.py0000644000076600000240000003004112426462410022656 0ustar jrocherstaff00000000000000 import unittest from numpy import array from numpy.testing import assert_array_almost_equal, assert_equal from chaco.api import ArrayDataSource, DataRange1D, LinearMapper class LinearMapperTestCase(unittest.TestCase): def test_basic(self): ary = array([5.0, 6.0, 7.0, 8.0, 9.0, 10.0]) ds = ArrayDataSource(ary) r = DataRange1D(ds) mapper = LinearMapper(range=r) self.assertFalse(mapper._low_bound_initialized) self.assertFalse(mapper._high_bound_initialized) mapper.low_pos=50 self.assertTrue(mapper._low_bound_initialized) mapper.high_pos=100 self.assertTrue(mapper._high_bound_initialized) result = mapper.map_screen(ary) assert_equal(result , array([50, 60, 70, 80, 90, 100])) return def test_reversed(self): ary = array([5.0, 6.0, 7.0, 8.0, 9.0, 10.0]) ds = ArrayDataSource(ary) r = DataRange1D(ds) mapper = LinearMapper(range=r) self.assertFalse(mapper._low_bound_initialized) self.assertFalse(mapper._high_bound_initialized) mapper.low_pos=100 self.assertTrue(mapper._low_bound_initialized) mapper.high_pos=0 self.assertTrue(mapper._high_bound_initialized) result = mapper.map_screen(ary) assert_equal(result , array([100, 80, 60, 40, 20, 0])) return def test_set_screen_bounds(self): ary = array([5.0, 6.0, 7.0, 8.0, 9.0, 10.0]) ds = ArrayDataSource(ary) r = DataRange1D(ds) mapper = LinearMapper(range=r) self.assertFalse(mapper._low_bound_initialized) self.assertFalse(mapper._high_bound_initialized) mapper.screen_bounds = (50.0, 100.0) self.assertTrue(mapper._low_bound_initialized) self.assertTrue(mapper._high_bound_initialized) result = mapper.map_screen(ary) assert_equal(result , array([50, 60, 70, 80, 90, 100])) return def test_reversed_set_screen_bounds(self): ary = array([5.0, 6.0, 7.0, 8.0, 9.0, 10.0]) ds = ArrayDataSource(ary) r = DataRange1D(ds) mapper = LinearMapper(range=r) self.assertFalse(mapper._low_bound_initialized) self.assertFalse(mapper._high_bound_initialized) mapper.screen_bounds = (100.0, 0.0) self.assertTrue(mapper._low_bound_initialized) self.assertTrue(mapper._high_bound_initialized) result = mapper.map_screen(ary) assert_equal(result , array([100, 80, 60, 40, 20, 0])) return def test_update_screen_bounds_stretch_data(self): ary = array([5.0, 6.0, 7.0, 8.0, 9.0, 10.0]) ds = ArrayDataSource(ary) r = DataRange1D(ds) mapper = LinearMapper(range=r, stretch_data=True) # Initialize the bounds, then modify them. mapper.screen_bounds = (50.0, 100.0) mapper.screen_bounds = (40.0, 120.0) result = mapper.map_screen(ary) assert_array_almost_equal( result, array([40.0, 56.0, 72.0, 88.0, 104.0, 120.0])) def test_update_screen_bounds_dont_stretch_data(self): ary = array([5.0, 6.0, 7.0, 8.0, 9.0, 10.0]) ds = ArrayDataSource(ary) r = DataRange1D(ds) mapper = LinearMapper(range=r, stretch_data=False) # Initialize the bounds, then modify them. mapper.screen_bounds = (50.0, 100.0) mapper.screen_bounds = (40.0, 120.0) result = mapper.map_screen(ary) assert_array_almost_equal( result, array([40.0, 50.0, 60.0, 70.0, 80.0, 90.0])) def test_reversed_update_screen_bounds_stretch_data(self): ary = array([5.0, 6.0, 7.0, 8.0, 9.0, 10.0]) ds = ArrayDataSource(ary) r = DataRange1D(ds) mapper = LinearMapper(range=r, stretch_data=True) # Initialize the bounds, then modify them. mapper.screen_bounds = (100.0, 0.0) mapper.screen_bounds = (120.0, -10.0) result = mapper.map_screen(ary) assert_array_almost_equal( result, array([120.0, 94.0, 68.0, 42.0, 16.0, -10.0])) def test_reversed_update_screen_bounds_dont_stretch_data(self): ary = array([5.0, 6.0, 7.0, 8.0, 9.0, 10.0]) ds = ArrayDataSource(ary) r = DataRange1D(ds) mapper = LinearMapper(range=r, stretch_data=False) # Initialize the bounds, then modify them. mapper.screen_bounds = (100.0, 0.0) mapper.screen_bounds = (120.0, -10.0) result = mapper.map_screen(ary) assert_array_almost_equal( result, array([120.0, 100.0, 80.0, 60.0, 40.0, 20.0])) def test_update_low_pos_stretch_data(self): ary = array([5.0, 6.0, 7.0, 8.0, 9.0, 10.0]) ds = ArrayDataSource(ary) r = DataRange1D(ds) mapper = LinearMapper(range=r, stretch_data=True) # Initialize the bounds, then modify them. mapper.screen_bounds = (50.0, 100.0) mapper.low_pos = 40.0 result = mapper.map_screen(ary) assert_array_almost_equal(result, array([40, 52, 64, 76, 88, 100])) def test_update_low_pos_dont_stretch_data(self): ary = array([5.0, 6.0, 7.0, 8.0, 9.0, 10.0]) ds = ArrayDataSource(ary) r = DataRange1D(ds) mapper = LinearMapper(range=r, stretch_data=False) # Initialize the bounds, then modify them. mapper.screen_bounds = (50.0, 100.0) mapper.low_pos = 40.0 result = mapper.map_screen(ary) assert_array_almost_equal(result, array([40, 50, 60, 70, 80, 90])) def test_reversed_update_low_pos_stretch_data(self): ary = array([5.0, 6.0, 7.0, 8.0, 9.0, 10.0]) ds = ArrayDataSource(ary) r = DataRange1D(ds) mapper = LinearMapper(range=r, stretch_data=True) # Initialize the bounds, then modify them. mapper.screen_bounds = (100.0, 50.0) mapper.low_pos = 110.0 result = mapper.map_screen(ary) assert_array_almost_equal(result, array([110, 98, 86, 74, 62, 50])) def test_reversed_update_low_pos_dont_stretch_data(self): ary = array([5.0, 6.0, 7.0, 8.0, 9.0, 10.0]) ds = ArrayDataSource(ary) r = DataRange1D(ds) mapper = LinearMapper(range=r, stretch_data=False) # Initialize the bounds, then modify them. mapper.screen_bounds = (100.0, 50.0) mapper.low_pos = 110.0 result = mapper.map_screen(ary) assert_array_almost_equal(result, array([110, 100, 90, 80, 70, 60])) def test_update_high_pos_stretch_data(self): ary = array([5.0, 6.0, 7.0, 8.0, 9.0, 10.0]) ds = ArrayDataSource(ary) r = DataRange1D(ds) mapper = LinearMapper(range=r, stretch_data=True) # Initialize the bounds, then modify them. mapper.screen_bounds = (50.0, 100.0) mapper.high_pos = 110.0 result = mapper.map_screen(ary) assert_array_almost_equal(result, array([50, 62, 74, 86, 98, 110])) def test_update_high_pos_dont_stretch_data(self): ary = array([5.0, 6.0, 7.0, 8.0, 9.0, 10.0]) ds = ArrayDataSource(ary) r = DataRange1D(ds) mapper = LinearMapper(range=r, stretch_data=False) # Initialize the bounds, then modify them. mapper.screen_bounds = (50.0, 100.0) mapper.high_pos = 110.0 result = mapper.map_screen(ary) assert_array_almost_equal(result, array([50, 60, 70, 80, 90, 100])) def test_reversed_update_high_pos_stretch_data(self): ary = array([5.0, 6.0, 7.0, 8.0, 9.0, 10.0]) ds = ArrayDataSource(ary) r = DataRange1D(ds) mapper = LinearMapper(range=r, stretch_data=True) # Initialize the bounds, then modify them. mapper.screen_bounds = (100.0, 50.0) mapper.high_pos = 40.0 result = mapper.map_screen(ary) assert_array_almost_equal(result, array([100, 88, 76, 64, 52, 40])) def test_reversed_update_high_pos_dont_stretch_data(self): ary = array([5.0, 6.0, 7.0, 8.0, 9.0, 10.0]) ds = ArrayDataSource(ary) r = DataRange1D(ds) mapper = LinearMapper(range=r, stretch_data=False) # Initialize the bounds, then modify them. mapper.screen_bounds = (100.0, 50.0) mapper.high_pos = 40.0 result = mapper.map_screen(ary) assert_array_almost_equal(result, array([100, 90, 80, 70, 60, 50])) def test_update_low_pos_stretch_data_with_reverse(self): ary = array([5.0, 6.0, 7.0, 8.0, 9.0, 10.0]) ds = ArrayDataSource(ary) r = DataRange1D(ds) mapper = LinearMapper(range=r, stretch_data=True) # Initialize the bounds, then modify them. mapper.screen_bounds = (50.0, 100.0) mapper.low_pos = 150.0 result = mapper.map_screen(ary) assert_array_almost_equal(result, array([150, 140, 130, 120, 110, 100])) def test_update_low_pos_dont_stretch_data_with_reverse(self): ary = array([5.0, 6.0, 7.0, 8.0, 9.0, 10.0]) ds = ArrayDataSource(ary) r = DataRange1D(ds) mapper = LinearMapper(range=r, stretch_data=False) # Initialize the bounds, then modify them. mapper.screen_bounds = (50.0, 100.0) mapper.low_pos = 150.0 result = mapper.map_screen(ary) assert_array_almost_equal(result, array([150, 160, 170, 180, 190, 200])) def test_update_high_pos_stretch_data_with_reverse(self): ary = array([5.0, 6.0, 7.0, 8.0, 9.0, 10.0]) ds = ArrayDataSource(ary) r = DataRange1D(ds) mapper = LinearMapper(range=r, stretch_data=True) # Initialize the bounds, then modify them. mapper.screen_bounds = (50.0, 100.0) mapper.high_pos = 0.0 result = mapper.map_screen(ary) assert_array_almost_equal(result, array([50, 40, 30, 20, 10, 0])) def test_update_high_pos_dont_stretch_data_with_reverse(self): ary = array([5.0, 6.0, 7.0, 8.0, 9.0, 10.0]) ds = ArrayDataSource(ary) r = DataRange1D(ds) mapper = LinearMapper(range=r, stretch_data=False) # Initialize the bounds, then modify them. mapper.screen_bounds = (50.0, 100.0) mapper.high_pos = 0.0 result = mapper.map_screen(ary) assert_array_almost_equal(result, array([50, 60, 70, 80, 90, 100])) def test_update_low_pos_stretch_data_with_zero(self): ary = array([5.0, 6.0, 7.0, 8.0, 9.0, 10.0]) ds = ArrayDataSource(ary) r = DataRange1D(ds) mapper = LinearMapper(range=r, stretch_data=True) # Initialize the bounds, then modify them. mapper.screen_bounds = (50.0, 100.0) mapper.low_pos = 100.0 result = mapper.map_screen(ary) assert_array_almost_equal(result, array([100, 100, 100, 100, 100, 100])) def test_update_low_pos_dont_stretch_data_with_zero(self): ary = array([5.0, 6.0, 7.0, 8.0, 9.0, 10.0]) ds = ArrayDataSource(ary) r = DataRange1D(ds) mapper = LinearMapper(range=r, stretch_data=False) # Initialize the bounds, then modify them. mapper.screen_bounds = (50.0, 100.0) mapper.low_pos = 100.0 result = mapper.map_screen(ary) assert_array_almost_equal(result, array([100, 100, 100, 100, 100, 100])) def test_update_high_pos_stretch_data_with_zero(self): ary = array([5.0, 6.0, 7.0, 8.0, 9.0, 10.0]) ds = ArrayDataSource(ary) r = DataRange1D(ds) mapper = LinearMapper(range=r, stretch_data=True) # Initialize the bounds, then modify them. mapper.screen_bounds = (50.0, 100.0) mapper.high_pos = 50.0 result = mapper.map_screen(ary) assert_array_almost_equal(result, array([50, 50, 50, 50, 50, 50])) def test_update_high_pos_dont_stretch_data_with_zero(self): ary = array([5.0, 6.0, 7.0, 8.0, 9.0, 10.0]) ds = ArrayDataSource(ary) r = DataRange1D(ds) mapper = LinearMapper(range=r, stretch_data=False) # Initialize the bounds, then modify them. mapper.screen_bounds = (50.0, 100.0) mapper.high_pos = 100.0 result = mapper.map_screen(ary) assert_array_almost_equal(result, array([50, 60, 70, 80, 90, 100])) if __name__ == '__main__': import nose nose.run() chaco-4.5.0/chaco/tests/logmapper_test_case.py0000644000076600000240000000521412426452312022171 0ustar jrocherstaff00000000000000 import unittest from numpy import array, nan from numpy.testing import assert_array_almost_equal, assert_equal from chaco.api import ArrayDataSource, DataRange1D, LogMapper class LogMapperTestCase(unittest.TestCase): def test_basic(self): ary = array([1.0, 10.0, 100.0, 1000.0, 10000.0]) ds = ArrayDataSource(ary) r = DataRange1D(ds) mapper = LogMapper(range=r, low_pos=50, high_pos=90) result = mapper.map_screen(ary) assert_equal(result, array([50, 60, 70, 80, 90])) return def test_reversed(self): ary = array([1.0, 10.0, 100.0, 1000.0, 10000.0]) ds = ArrayDataSource(ary) r = DataRange1D(ds) mapper = LogMapper(range=r, low_pos=100, high_pos=0) result = mapper.map_screen(ary) assert_array_almost_equal(result, array([100, 75, 50, 25, 0])) return def test_fractional(self): ary = array([0.0001, 0.001, 0.01]) ds = ArrayDataSource(ary) r = DataRange1D(ds) mapper = LogMapper(range=r, low_pos=0, high_pos=20) result = mapper.map_screen(ary) assert_array_almost_equal(result, [0, 10, 20]) return def test_zero(self): ary = array([0.0, 1.0, 10.0, 100.0, 1000.0]) ds = ArrayDataSource(ary) r = DataRange1D(ds) mapper = LogMapper(range=r, low_pos=0, high_pos=30) result = mapper.map_screen(ary) assert_array_almost_equal(result, [0, 0, 10, 20, 30]) return def test_negative(self): ary = array([1.0, -1.0, -2.0, 10.0, 100.0, 1000.0]) ds = ArrayDataSource(ary) r = DataRange1D(ds) mapper = LogMapper(range=r, low_pos=0, high_pos=30) result = mapper.map_screen(ary) assert_array_almost_equal(result, [0, 0, 0, 10, 20, 30]) return def test_fill_value(self): ary = array([1.0, -1.0, -2.0, 10.0, 100.0, 1000.0]) ds = ArrayDataSource(ary) r = DataRange1D(ds) mapper = LogMapper(range=r, low_pos=0, high_pos=30) # This causes out-of-bounds values to be treated as the value 100.0 mapper.fill_value = 100.0 result = mapper.map_screen(ary) assert_array_almost_equal(result, [0, 20, 20, 10, 20, 30]) return def test_nan(self): ary = array([1.0, nan, 10.0, nan, 100.0, 1000.0]) ds = ArrayDataSource(ary) r = DataRange1D(ds) mapper = LogMapper(range=r, low_pos=0, high_pos=30) mapper.fill_value = 100.0 result = mapper.map_screen(ary) assert_array_almost_equal(result, [0, 20, 10, 20, 20, 30]) return if __name__ == '__main__': import nose nose.run() chaco-4.5.0/chaco/tests/plot_test_case.py0000644000076600000240000000106212426462254021164 0ustar jrocherstaff00000000000000import unittest from numpy import arange # Chaco imports from chaco.api import ArrayPlotData, Plot class PlotTestCase(unittest.TestCase): def test_plot_from_unsupported_array_shape(self): arr = arange(8).reshape(2, 2, 2) data = ArrayPlotData(x=arr, y=arr) plot = Plot(data) self.assertRaises(ValueError, plot.plot, ("x", "y")) arr = arange(16).reshape(2, 2, 2, 2) data.update_data(x=arr, y=arr) self.assertRaises(ValueError, plot.plot, ("x", "y")) if __name__ == "__main__": unittest.main() chaco-4.5.0/chaco/tests/plotcontainer_test_case.py0000644000076600000240000006571512426452312023100 0ustar jrocherstaff00000000000000import sys import unittest from chaco.api import HPlotContainer, OverlayPlotContainer, \ PlotComponent, VPlotContainer, GridContainer from traits.api import Any, Tuple SizePrefs = GridContainer.SizePrefs class ContainerTestCase(unittest.TestCase): def assert_tuple(self, t1, t2): self.assertEquals(len(t1), len(t2)) for i in xrange(len(t1)): self.assertEquals(t1[i], t2[i]) class StaticPlotComponent(PlotComponent): """ A plotcomponent with fixed dimensions """ def __init__(self, bounds, *args, **kw): kw["bounds"] = bounds if not kw.has_key("resizable"): kw["resizable"] = "" PlotComponent.__init__(self, *args, **kw) return class ResizablePlotComponent(PlotComponent): """ A resizable PlotComponent with a fixed preferred size. """ # An optional trait for expressing the preferred size of this component, # regardless of whether or not it is resizable. fixed_preferred_size = Any # Override default value in PlotComponent resizable = "hv" def __init__(self, preferred_size=None, *args, **kw): if preferred_size is not None: self.fixed_preferred_size = preferred_size PlotComponent.__init__(self, *args, **kw) def get_preferred_size(self): if self.fixed_preferred_size is not None: return self.fixed_preferred_size else: return PlotComponent.get_preferred_size(self) class OverlayPlotContainerTestCase(ContainerTestCase): def test_basics(self): container = OverlayPlotContainer(resizable='', bounds=[100.0,200.0]) self.assert_tuple(container.get_preferred_size(), (100.0,200.0)) self.assertEquals(container._layout_needed, True) container.do_layout() self.assertEquals(container._layout_needed, False) return def test_fixed_size_component(self): container = OverlayPlotContainer(resizable='', bounds=[200.0,300.0]) # non-resizable component component = PlotComponent(resizable='', position=[50.0,60.0], bounds=[100.0,110.0]) self.assertEquals(container._layout_needed, True) container.do_layout() container.add(component) self.assertEquals(container._layout_needed, True) container.do_layout() self.assertEquals(container._layout_needed, False) # check the results of the layout self.assert_tuple(container.get_preferred_size(), (200.0,300.0)) self.assert_tuple(component.position, (50.0,60.0)) self.assert_tuple(component.bounds, (100.0,110.0)) return def test_resizable_component(self): container = OverlayPlotContainer(resizable='', bounds=[200.0,300.0]) component = PlotComponent(resizable='hv', position=[50.0,56.0], bounds=[100.0,110.0]) container.add(component) container.do_layout() self.assert_tuple(component.position, (0.0,0.0)) self.assert_tuple(component.bounds, (200.0,300.0)) comp2 = PlotComponent(resizable="h", position=[10,20], bounds=[100,150]) container.add(comp2) container.do_layout() self.assert_tuple(comp2.position, (0.0, 20.0)) self.assert_tuple(comp2.bounds, (200.0, 150.0)) comp3 = PlotComponent(resizable="v", position=[30,40], bounds=[100,150]) container.add(comp3) container.do_layout() self.assert_tuple(comp3.position, (30.0, 0.0)) self.assert_tuple(comp3.bounds, (100,300)) return def test_min_size(self): container = OverlayPlotContainer(resizable='', bounds=[50.0,50.0]) component = PlotComponent(resizable='', position=[50.0,60.0], bounds=[100.0, 110.0]) container.add(component) container.do_layout() self.assert_tuple(component.position, (50.0,60.0)) self.assert_tuple(component.bounds, (100.0,110.0)) return def test_multiple_min_size(self): comp1 = StaticPlotComponent([200, 50]) comp2 = StaticPlotComponent([60, 300]) container = OverlayPlotContainer(resizable='hv', bounds=[30,30]) container.fit_components = "hv" container.add(comp1, comp2) container.do_layout() self.assert_tuple(container.get_preferred_size(), (200,300)) self.assert_tuple(comp1.bounds, (200,50)) self.assert_tuple(comp2.bounds, (60,300)) return class HPlotContainerTestCase(ContainerTestCase): def test_stack_nonresize(self): # Assuming resizable='' for all plot containers and components container = HPlotContainer(bounds=[300,100]) comp1 = StaticPlotComponent([100,70]) comp2 = StaticPlotComponent([90,80]) comp3 = StaticPlotComponent([80,90]) container.add(comp1, comp2, comp3) container.do_layout() self.assert_tuple(container.get_preferred_size(), (270,90)) self.assert_tuple(container.bounds, (300,100)) self.assert_tuple(comp1.position, (0,0)) self.assert_tuple(comp2.position, (100,0)) self.assert_tuple(comp3.position, (190,0)) return def test_stack_one_resize(self): "Checks stacking with 1 resizable component thrown in" container = HPlotContainer(bounds=[300,100]) comp1 = StaticPlotComponent([100,70]) comp2 = StaticPlotComponent([90,80]) comp3 = StaticPlotComponent([80,90], resizable='hv') comp4 = StaticPlotComponent([40,50]) container.add(comp1, comp2, comp3, comp4) container.do_layout() self.assert_tuple(container.get_preferred_size(), (230,80)) self.assert_tuple(container.bounds, (300,100)) self.assert_tuple(comp1.position, (0,0)) self.assert_tuple(comp2.position, (100,0)) self.assert_tuple(comp3.position, (190,0)) self.assert_tuple(comp4.position, (260,0)) return def test_valign(self): container = HPlotContainer(bounds=[300,200], valign="center") comp1 = StaticPlotComponent([200,100]) container.add(comp1) container.do_layout() self.failUnlessEqual(comp1.position, [0,50]) container.valign="top" container.do_layout(force=True) self.failUnlessEqual(comp1.position, [0,100]) return class VPlotContainerTestCase(ContainerTestCase): # These tests are mostly transposes of the values in HPlotContainer def test_stack_nonresize(self): container = VPlotContainer(bounds=[100,300]) comp1 = StaticPlotComponent([70,100]) comp2 = StaticPlotComponent([80,90]) comp3 = StaticPlotComponent([90,80]) container.add(comp1, comp2, comp3) container.do_layout() self.assert_tuple(container.get_preferred_size(), (90, 270)) self.assert_tuple(container.bounds, (100,300)) self.assert_tuple(comp1.position, (0,0)) self.assert_tuple(comp2.position, (0,100)) self.assert_tuple(comp3.position, (0,190)) return def test_stack_one_resize(self): "Checks stacking with 1 resizable component thrown in" container = VPlotContainer(bounds=[100,300]) comp1 = StaticPlotComponent([70,100]) comp2 = StaticPlotComponent([80,90]) comp3 = StaticPlotComponent([90,80], resizable='hv') comp4 = StaticPlotComponent([50,40]) container.add(comp1, comp2, comp3, comp4) container.do_layout() self.assert_tuple(container.get_preferred_size(), (80,230)) self.assert_tuple(container.bounds, (100,300)) self.assert_tuple(comp1.position, (0,0)) self.assert_tuple(comp2.position, (0,100)) self.assert_tuple(comp3.position, (0,190)) self.assert_tuple(comp4.position, (0,260)) return def test_halign(self): container = VPlotContainer(bounds=[200,300], halign="center") comp1 = StaticPlotComponent([100,200]) container.add(comp1) container.do_layout() self.failUnlessEqual(comp1.position, [50,0]) container.halign="right" container.do_layout(force=True) self.failUnlessEqual(comp1.position, [100,0]) return def test_fit_components(self): container = VPlotContainer(bounds=[200,300], resizable="v", fit_components="v") comp1 = StaticPlotComponent([50,100], padding=5) comp2 = StaticPlotComponent([50,120], padding=5) container.add(comp1) container.add(comp2) self.assert_tuple(container.get_preferred_size(), (200,240)) # The container should not change its size as a result of its fit_components # being set. self.assert_tuple(container.bounds, (200,300)) container.bounds = container.get_preferred_size() container.do_layout() container.padding = 8 self.assert_tuple(container.get_preferred_size(), (216,256)) container.do_layout() self.assert_tuple(comp1.outer_position, (0,0)) self.assert_tuple(comp2.outer_position, (0,110)) class SizePrefsTestCase(unittest.TestCase): def assert_tuple(self, t1, t2): self.assertEquals(t1[0], t2[0]) self.assertEquals(t1[1], t2[1]) def test_sequential_non_resizable(self): prefs = SizePrefs(4, "h") components = [StaticPlotComponent([100,100]) for i in range(4)] for i, c in enumerate(components): prefs.update_from_component(c, i) pref_size = prefs.get_preferred_size() self.assert_tuple(pref_size, (100,100,100,100)) sizes = prefs.compute_size_array(400) self.assert_tuple(sizes, (100,100,100,100)) sizes2 = prefs.compute_size_array(500) self.assert_tuple(sizes, (100,100,100,100)) def test_overlapping_non_resizable(self): prefs = SizePrefs(1, "h") prefs.update_from_component(StaticPlotComponent([100,10]), 0) prefs.update_from_component(StaticPlotComponent([200,10]), 0) prefs.update_from_component(StaticPlotComponent([300,10]), 0) pref_size = prefs.get_preferred_size() self.assertEquals(pref_size[0], 300) sizes = prefs.compute_size_array(400) self.assertEquals(sizes[0], 400) def test_sequential_resizable(self): prefs = SizePrefs(3, "v") prefs.update_from_component(ResizablePlotComponent([10,100]), 0) prefs.update_from_component(ResizablePlotComponent([10,200]), 1) prefs.update_from_component(ResizablePlotComponent([10,300]), 2) pref_size = prefs.get_preferred_size() self.assert_tuple(pref_size, (100,200,300)) sizes = prefs.compute_size_array(600) self.assert_tuple(sizes, [100, 200, 300]) sizes2 = prefs.compute_size_array(60) self.assert_tuple(sizes2, [10, 20, 30]) sizes3 = prefs.compute_size_array(6000) self.assert_tuple(sizes3, [1000, 2000, 3000]) def test_overlapping_resizable(self): prefs = SizePrefs(2, "h") prefs.update_from_component(ResizablePlotComponent([50, 10]), 0) prefs.update_from_component(ResizablePlotComponent([100, 10]), 0) prefs.update_from_component(ResizablePlotComponent([80, 10]), 1) pref_size = prefs.get_preferred_size() self.assert_tuple(pref_size, (100,80)) sizes = prefs.compute_size_array(180) self.assert_tuple(sizes, (100, 80)) sizes2 = prefs.compute_size_array(360) self.assert_tuple(sizes2, (200, 160)) def test_sequential_fully_resizable(self): prefs = SizePrefs(3, "h") for i in range(3): prefs.update_from_component(ResizablePlotComponent(), i) pref_size = prefs.get_preferred_size() self.assert_tuple(pref_size, (0,0,0)) sizes = prefs.compute_size_array(60) self.assert_tuple(sizes, (20, 20, 20)) def test_overlapping_fully_resizable(self): prefs = SizePrefs(1, "h") for i in range(3): prefs.update_from_component(ResizablePlotComponent(), 0) pref_size = prefs.get_preferred_size() self.assertEquals(pref_size[0], 0) sizes = prefs.compute_size_array(60) self.assertEquals(sizes[0], 60) def test_sequential_mixed_resizable(self): # Tests a sequence of resizable and fully resizable components. prefs = SizePrefs(3, "h") prefs.update_from_component(ResizablePlotComponent(), 0) prefs.update_from_component(ResizablePlotComponent([100,10]), 1) prefs.update_from_component(ResizablePlotComponent(), 2) pref_size = prefs.get_preferred_size() self.assert_tuple(pref_size, (0, 100, 0)) sizes = prefs.compute_size_array(50) self.assert_tuple(sizes, (0, 50, 0)) sizes2 = prefs.compute_size_array(100) self.assert_tuple(sizes2, (0, 100, 0)) sizes3 = prefs.compute_size_array(200) self.assert_tuple(sizes3, (50, 100, 50)) def test_overlapping_mixed_resizable(self): # Tests a sequence of overlapping resizable and fully resizable components. prefs = SizePrefs(4, "h") # Slot 1 prefs.update_from_component(ResizablePlotComponent([100,10]), 0) prefs.update_from_component(ResizablePlotComponent(), 0) # Slot 2 prefs.update_from_component(ResizablePlotComponent(), 1) prefs.update_from_component(ResizablePlotComponent([50,10]), 1) # Slot 3 prefs.update_from_component(ResizablePlotComponent(), 2) prefs.update_from_component(ResizablePlotComponent([40,10]), 2) # Slot 4 prefs.update_from_component(ResizablePlotComponent(), 3) prefs.update_from_component(ResizablePlotComponent(), 3) pref_size = prefs.get_preferred_size() self.assert_tuple(pref_size, (100, 50, 40, 0)) sizes = prefs.compute_size_array(95) self.assert_tuple(sizes, (50, 25, 20, 0)) sizes2 = prefs.compute_size_array(230) self.assert_tuple(sizes2, (100, 50, 40, 40)) def test_sequential_mixed_resizable_static(self): # Tests a sequence of static and resizable components. prefs = SizePrefs(3, "h") prefs.update_from_component(StaticPlotComponent([100,10]), 0) prefs.update_from_component(ResizablePlotComponent([50,10]), 1) prefs.update_from_component(ResizablePlotComponent([75,10]), 2) pref_size = prefs.get_preferred_size() self.assert_tuple(pref_size, (100,50,75)) sizes = prefs.compute_size_array(225) self.assert_tuple(sizes, (100,50,75)) sizes2 = prefs.compute_size_array(350) self.assert_tuple(sizes2, (100,100,150)) def test_sequential_mixed_resizable_static2(self): # Tests a sequence of non-overlapping static, resizable, and fully # resizable components. prefs = SizePrefs(4, "h") prefs.update_from_component(StaticPlotComponent([100,10]), 0) prefs.update_from_component(ResizablePlotComponent([50,10]), 1) prefs.update_from_component(ResizablePlotComponent([75,10]), 2) prefs.update_from_component(ResizablePlotComponent(), 3) pref_size = prefs.get_preferred_size() self.assert_tuple(pref_size, (100,50,75,0)) sizes = prefs.compute_size_array(300) self.assert_tuple(sizes, (100,50,75,75)) def test_overlapping_mixed_resizable_static(self): prefs = SizePrefs(5, "h") # Slot 1 - static and smaller resizable prefs.update_from_component(StaticPlotComponent([100,10]), 0) prefs.update_from_component(ResizablePlotComponent([50,10]), 0) # Slot 2 - static and larger resizable prefs.update_from_component(StaticPlotComponent([30,10]), 1) prefs.update_from_component(ResizablePlotComponent([60,10]), 1) # Slot 3 - static and fully resizable prefs.update_from_component(StaticPlotComponent([50,10]), 2) prefs.update_from_component(ResizablePlotComponent(), 2) # Slot 4 - resizable and fully resizable prefs.update_from_component(ResizablePlotComponent([90,10]), 3) prefs.update_from_component(ResizablePlotComponent(), 3) # Slot 5 - fully resizable prefs.update_from_component(ResizablePlotComponent(), 4) pref_size = prefs.get_preferred_size() self.assert_tuple(pref_size, (100, 60, 50, 90, 0)) # Test scaling down of resizable components in slots 2 and 4 sizes = prefs.compute_size_array(180 + 60) self.assert_tuple(sizes, (100, 30+15, 50, 45, 0)) # Test scaling up of fully resizable component in slot 5, and proper # allocation of slot 2's resizable component's full preferred size. sizes2 = prefs.compute_size_array(300 + 35) self.assert_tuple(sizes2, (100, 60, 50, 90, 35)) class GridContainerTestCase(ContainerTestCase): def test_empty_container(self): cont = GridContainer(shape=(1,1)) cont.bounds = [100,100] cont.do_layout() return def test_all_empty_cells(self): cont = GridContainer(shape=(2,2), spacing=(0,0)) cont.component_grid = [[None, None], [None, None]] size = cont.get_preferred_size() self.assert_tuple(size, (0,0)) cont.bounds = (100,100) cont.do_layout() return def test_some_empty_cells(self): cont = GridContainer(shape=(2,2), spacing=(0,0)) a = StaticPlotComponent([100,30]) b = StaticPlotComponent([50,40]) cont.component_grid = [[a, None], [None, b]] size = cont.get_preferred_size() self.assert_tuple(size, (150, 70)) cont.bounds = size cont.do_layout() self.assert_tuple(a.outer_position, (0, 40)) self.assert_tuple(a.outer_bounds, (100, 30)) self.assert_tuple(b.outer_position, (100,0)) self.assert_tuple(b.outer_bounds, (50, 40)) def test_single_cell(self): cont = GridContainer(shape=(1,1)) comp1 = StaticPlotComponent([200,300]) cont.add(comp1) cont.do_layout() # it would be nice to make all boolean tests here trigger # assert failures, maybe using Pypy? self.assert_tuple(comp1.position, (0,0)) self.assert_tuple(comp1.bounds, (200,300)) return def test_nonresizable_container(self): cont = GridContainer(shape=(1,1), resizable="") comp1 = StaticPlotComponent([200,300]) cont.add(comp1) cont.do_layout() self.assert_tuple(comp1.position, (0,0)) self.assert_tuple(comp1.bounds, (200,300)) return def test_row(self): cont = GridContainer(shape=(1,3), halign="center", valign="center") c1 = StaticPlotComponent([50,50]) c2 = StaticPlotComponent([30,30]) c3 = StaticPlotComponent([0,0], resizable="hv") cont.add(c1, c2, c3) cont.bounds = list(cont.get_preferred_size()) cont.do_layout() self.assert_tuple(c1.position, (0,0)) self.assert_tuple(c1.bounds, (50,50)) self.assert_tuple(c2.position, (50,10)) self.assert_tuple(c2.bounds, (30,30)) self.assert_tuple(c3.position, (80,0)) self.assert_tuple(c3.bounds, (0,50)) cont.bounds = [100, 50] cont.do_layout() self.assert_tuple(c1.position, (0,0)) self.assert_tuple(c1.bounds, (50,50)) self.assert_tuple(c2.position, (50,10)) self.assert_tuple(c2.bounds, (30,30)) self.assert_tuple(c3.position, (80,0)) self.assert_tuple(c3.bounds, (20,50)) return def test_two_by_two(self): """ Tests a 2x2 grid of components """ cont = GridContainer(shape=(2,2), halign="center", valign="center") ul = StaticPlotComponent([50,50]) # upper-left component lr = StaticPlotComponent([100,100]) # lower-right component top = StaticPlotComponent([0,0], resizable="hv") left = StaticPlotComponent([0,0], resizable="hv") cont.component_grid = [[ul, top], [left, lr]] cont.bounds = [150, 150] cont.do_layout() self.assert_tuple(ul.position, (0,100)) self.assert_tuple(ul.bounds, (50,50)) self.assert_tuple(top.position, (50,100)) self.assert_tuple(top.bounds, (100, 50)) self.assert_tuple(left.position, (0,0)) self.assert_tuple(left.bounds, (50,100)) self.assert_tuple(lr.position, (50,0)) self.assert_tuple(lr.bounds, (100,100)) return def test_spacing(self): cont = GridContainer(shape=(2,2), spacing=(10,10), halign="center", valign="center") ul = StaticPlotComponent([50,50]) # upper-left component lr = StaticPlotComponent([100,100]) # lower-right component top = StaticPlotComponent([0,0], resizable="hv") left = StaticPlotComponent([0,0], resizable="hv") cont.component_grid = [[ul, top], [left, lr]] cont.bounds = [190, 190] cont.do_layout() self.assert_tuple(ul.position, (10,130)) self.assert_tuple(ul.bounds, (50,50)) self.assert_tuple(top.position, (80,130)) self.assert_tuple(top.bounds, (100, 50)) self.assert_tuple(left.position, (10,10)) self.assert_tuple(left.bounds, (50,100)) self.assert_tuple(lr.position, (80,10)) self.assert_tuple(lr.bounds, (100,100)) return def test_resizable(self): cont = GridContainer(shape=(2,2), spacing=(0,0), halign="center", valign="center") ul = StaticPlotComponent([0,0], resizable="hv") lr = StaticPlotComponent([0,0], resizable="hv") top = StaticPlotComponent([0,0], resizable="hv") left = StaticPlotComponent([0,0], resizable="hv") cont.component_grid = [[ul, top], [left, lr]] cont.bounds = [200, 200] cont.do_layout() self.assert_tuple(ul.position, (0,100)) self.assert_tuple(ul.bounds, (100,100)) self.assert_tuple(top.position, (100,100)) self.assert_tuple(top.bounds, (100, 100)) self.assert_tuple(left.position, (0,0)) self.assert_tuple(left.bounds, (100,100)) self.assert_tuple(lr.position, (100,0)) self.assert_tuple(lr.bounds, (100,100)) return def test_resizable2(self): # Tests a resizable component that also has a preferred size cont = GridContainer(shape=(2,2), spacing=(0,0), halign="center", valign="center") ul = StaticPlotComponent([150,150], resizable="hv") lr = StaticPlotComponent([0,0], resizable="hv") top = StaticPlotComponent([0,0], resizable="hv") left = StaticPlotComponent([0,0], resizable="hv") cont.component_grid = [[ul, top], [left, lr]] cont.bounds = [200, 200] cont.do_layout() self.assert_tuple(ul.position, (0,100)) self.assert_tuple(ul.bounds, (100,100)) self.assert_tuple(top.position, (100,100)) self.assert_tuple(top.bounds, (100,100)) self.assert_tuple(left.position, (0,0)) self.assert_tuple(left.bounds, (100,100)) self.assert_tuple(lr.position, (100,0)) self.assert_tuple(lr.bounds, (100,100)) def test_resizable_mixed(self): """ Tests mixing resizable and non-resizable components """ cont = GridContainer(shape=(2,2), spacing=(10,10), halign="center", valign="center") ul = StaticPlotComponent([0,0], resizable="hv") lr = StaticPlotComponent([0,0], resizable="hv") top = StaticPlotComponent([0,0], resizable="hv") left = StaticPlotComponent([100,100], resizable="") cont.component_grid = [[ul, top], [left, lr]] cont.bounds = [240, 240] cont.do_layout() self.assert_tuple(ul.position, (10,130)) self.assert_tuple(ul.bounds, (100,100)) self.assert_tuple(top.position, (130,130)) self.assert_tuple(top.bounds, (100, 100)) self.assert_tuple(left.position, (10,10)) self.assert_tuple(left.bounds, (100,100)) self.assert_tuple(lr.position, (130,10)) self.assert_tuple(lr.bounds, (100,100)) return def test_resizable_mixed2(self): # Tests laying out resizable components with preferred # sized alongside non-resizable components. cont = GridContainer(shape=(2,2), spacing=(0,0), halign="center", valign="center") ul = ResizablePlotComponent([150,150]) lr = StaticPlotComponent([50,50], resizable="") top = StaticPlotComponent([0,0], resizable="hv") left = StaticPlotComponent([0,0], resizable="hv") cont.component_grid = [[ul, top], [left, lr]] cont.bounds = [200, 200] cont.do_layout() self.assert_tuple(ul.position, (0,50)) self.assert_tuple(ul.bounds, (150,150)) self.assert_tuple(top.position, (150,50)) self.assert_tuple(top.bounds, (50,150)) self.assert_tuple(left.position, (0,0)) self.assert_tuple(left.bounds, (150,50)) self.assert_tuple(lr.position, (150,0)) self.assert_tuple(lr.bounds, (50,50)) def test_resizable_mixed_h(self): # Tests the layout of a non-resizable component, a resizable with a # preferred size, and a fully resizable component in a horizontal # GridContainer cont = GridContainer(shape=(3,1), spacing=(0,0), halign="center", valign="center") left = StaticPlotComponent([50,10], resizable="") middle = ResizablePlotComponent([100,10]) right = StaticPlotComponent([0,0], resizable="hv") cont.component_grid = [[left, middle, right]] cont.bounds = [200, 10] cont.do_layout() self.assert_tuple(left.position, (0,0)) self.assert_tuple(left.bounds, (50,10)) self.assert_tuple(middle.position, (50,0)) self.assert_tuple(middle.bounds, (100,10)) self.assert_tuple(right.position, (150,0)) self.assert_tuple(right.bounds, (50,10)) def test_non_resizable(self): cont = GridContainer(shape=(2,2), spacing=(10,10), halign="center", valign="center") ul = StaticPlotComponent([100,100], resizable="") ur = StaticPlotComponent([100,100], resizable="") ll = StaticPlotComponent([100,100], resizable="") lr = StaticPlotComponent([100,100], resizable="") cont.component_grid = [[ul, ur], [ll, lr]] cont.bounds = [240, 240] cont.do_layout() self.assert_tuple(ul.position, (10,130)) self.assert_tuple(ul.bounds, (100,100)) self.assert_tuple(ur.position, (130,130)) self.assert_tuple(ur.bounds, (100, 100)) self.assert_tuple(ll.position, (10,10)) self.assert_tuple(ll.bounds, (100,100)) self.assert_tuple(lr.position, (130,10)) self.assert_tuple(lr.bounds, (100,100)) cont.bounds = [280, 280] cont.do_layout() self.assert_tuple(ul.position, (20,160)) self.assert_tuple(ul.bounds, (100,100)) self.assert_tuple(ur.position, (160,160)) self.assert_tuple(ur.bounds, (100, 100)) self.assert_tuple(ll.position, (20,20)) self.assert_tuple(ll.bounds, (100,100)) self.assert_tuple(lr.position, (160,20)) self.assert_tuple(lr.bounds, (100,100)) if __name__ == '__main__': import nose nose.run() chaco-4.5.0/chaco/tests/scatterplot_renderers_test_case.py0000644000076600000240000000460012426452312024616 0ustar jrocherstaff00000000000000import unittest from numpy import alltrue from enable.compiled_path import CompiledPath # Chaco imports from chaco.api import create_scatter_plot, PlotGraphicsContext class DrawScatterplotCase(unittest.TestCase): def test_scatter_fast(self): """ Coverage test to check basic case works """ size = (50, 50) scatterplot = create_scatter_plot( data=[range(10), range(10)], border_visible=False, ) scatterplot.outer_bounds = list(size) gc = PlotGraphicsContext(size) gc.render_component(scatterplot) actual = gc.bmp_array[:, :, :] self.assertFalse(alltrue(actual == 255)) def test_scatter_circle(self): """ Coverage test to check circles work """ size = (50, 50) scatterplot = create_scatter_plot( data=[range(10), range(10)], marker="circle", border_visible=False, ) scatterplot.outer_bounds = list(size) gc = PlotGraphicsContext(size) gc.render_component(scatterplot) actual = gc.bmp_array[:, :, :] self.assertFalse(alltrue(actual == 255)) def test_scatter_custom(self): """ Coverage test to check custom markers work """ # build path path = CompiledPath() path.move_to(-5, -5) path.line_to(5, 5) path.line_to(5, -5) path.line_to(-5, 5) path.line_to(-5, -5) size = (50, 50) scatterplot = create_scatter_plot( data=[range(10), range(10)], marker='custom', border_visible=False, ) scatterplot.custom_symbol = path scatterplot.outer_bounds = list(size) gc = PlotGraphicsContext(size) gc.render_component(scatterplot) actual = gc.bmp_array[:, :, :] self.assertFalse(alltrue(actual == 255)) def test_scatter_slow(self): """ Coverage test to check multiple marker size works """ size = (50, 50) scatterplot = create_scatter_plot( data=[range(10), range(10)], border_visible=False, marker_size=range(1, 11), ) scatterplot.outer_bounds = list(size) gc = PlotGraphicsContext(size) gc.render_component(scatterplot) actual = gc.bmp_array[:, :, :] self.assertFalse(alltrue(actual == 255)) if __name__ == "__main__": unittest.main() chaco-4.5.0/chaco/tests/serializable_base.py0000644000076600000240000000103512426452312021606 0ustar jrocherstaff00000000000000 from traits.api import Bool, HasTraits, Str, Float, Enum, List, Int from chaco.serializable import Serializable class Root(HasTraits): name = Str x = Float(0.0) y = Float(0.0) class Shape(Serializable, Root): color = Enum("red", "green", "blue") filled = Bool(True) tools = List _pickles = ("tools", "filled", "color", "x") class Circle(Shape): radius = Float(10.0) _pickles = ("radius",) class Poly(Shape): numsides = Int(5) length = Float(5.0) _pickles = ("numsides", "length") # EOF chaco-4.5.0/chaco/tests/serializable_test_case.py0000644000076600000240000000313012426452312022644 0ustar jrocherstaff00000000000000 from cPickle import loads, dumps import unittest # pickling child classes doesn't work well in the unittest framework unless # the classes to be pickled are in a different file from serializable_base import Circle, Poly class SimpleSerializationTestCase(unittest.TestCase): def compare_traits(self, a, b, trait_names=None): "Checks the traits of objects 'a' and 'b' and makes sure they all match." if trait_names is None: trait_names = a.trait_names() for name in trait_names: if name in ("trait_added", "trait_modified"): continue o1 = getattr(a,name) o2 = getattr(b,name) if isinstance(o1, list) or isinstance(o1, tuple): print "Warning: Cowardly refusing to do deep compares" else: self.assert_(o1 == o2) return def test_basic_save(self): c = Circle(radius=5.0, name="c1", x=1.0, y=2.0) c2 = loads(dumps(c)) for attrib in ("tools", "filled", "color", "x", "radius"): self.assert_(getattr(c, attrib) == getattr(c2, attrib)) self.failUnlessEqual(c2.y, 2.0) return def test_basic_save2(self): p = Poly(numside=3, name="poly", x=3.0, y=4.0) p2 = loads(dumps(p)) for attrib in ("tools", "filled", "color", "x", "numsides", "length"): self.assert_(getattr(p, attrib) == getattr(p2, attrib)) self.failUnlessEqual(p2.y, 4.0) return class PlotSerializationTestCase(unittest.TestCase): pass if __name__ == '__main__': import nose nose.run() chaco-4.5.0/chaco/tests/speedups_test_case.py0000644000076600000240000000511412426452312022032 0ustar jrocherstaff00000000000000 import unittest from numpy import alltrue, array, ravel, zeros, isinf, linspace #from chaco import _speedups as speedups #from chaco import _speedups_fallback as fallback def assert_close(desired,actual): diff_allowed = 1e-5 diff = abs(ravel(actual) - ravel(desired)) for d in diff: if not isinf(d): assert alltrue(d <= diff_allowed) return class GatherPointsBase(object): # The module to look for the gather_points function in; subclasses # should override this. module = None def test_basic(self): index = linspace(0.0, 20.0, 21) value = linspace(0.0, 1.0, 21) points, selection = self.func(index, 4.5, 14.5, value, -1.0, 2.4) desired = array([[5, 6, 7, 8, 9, 10, 11, 12, 13, 14], [0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.8]]).T self.assert_(selection == None) assert_close(desired, points) def test_masked(self): index = linspace(0.0, 10.0, 11) value = linspace(0.0, 1.0, 11) index_mask = zeros(11, dtype=bool) index_mask[2:6] = 1 value_mask = zeros(11, dtype=bool) value_mask[4:8] = 1 points, selection = self.func(index, 0, 10, value, 0, 1, index_mask = index_mask) desired = array([[2, 3, 4, 5], [0.2, 0.3, 0.4, 0.5]]).T assert_close(desired, points) points, selection = self.func(index, 0, 10, value, 0, 1, index_mask = index_mask, value_mask = value_mask) desired = array([[4, 0.4], [5, 0.5]]) assert_close(desired, points) def test_selection(self): pass def test_selection_range(self): pass def _get_func(self): return self.module.scatterplot_gather_points func = property(_get_func) #class SpeedupsTestCase(GatherPointsBase, unittest.TestCase): # module = speedups #class SpeedupsFallbackTestCase(SpeedupsTestCase): # module = fallback #def timing_test_gather_points(): # import time # from numpy import sin, pi # numpoints = 10000 # numruns = 10 # x = linspace(-8*pi, 8*pi, numpoints) # y = sin(x) # args = (x, -30, 30, y, -0.5, 0.5) # funcs = {"Fallback": fallback.scatterplot_gather_points, # "C-based": speedups.scatterplot_gather_points} # # for name, func in funcs.items(): # now = time.time() # for i in range(numruns): # points, selection = func(*args) # print "%s (%d pts, %d runs):" % (name, numpoints, numruns), (time.time() - now) # return chaco-4.5.0/chaco/tests/test_cmap_image_plot.py0000644000076600000240000000237312426462410022333 0ustar jrocherstaff00000000000000import unittest import mock import numpy from enable.api import AbstractWindow from chaco.api import ( CMapImagePlot, DataRange1D, DataRange2D, GridDataSource, GridMapper, ImageData) from chaco.default_colormaps import Spectral class TestCMapImagePlot(unittest.TestCase): def test_redraw_on_color_mapper_update(self): # regression check for https://github.com/enthought/chaco/issues/220 npoints = 200 xs = numpy.linspace(-2 * numpy.pi, +2 * numpy.pi, npoints) ys = numpy.linspace(-1.5*numpy.pi, +1.5*numpy.pi, npoints) x, y = numpy.meshgrid(xs, ys) z = y * x index = GridDataSource(xdata=xs, ydata=ys) index_mapper = GridMapper(range=DataRange2D(index)) color_source = ImageData(data=z, value_depth=1) color_mapper = Spectral(DataRange1D(color_source)) cmap_plot = CMapImagePlot( index=index, index_mapper=index_mapper, value=color_source, value_mapper=color_mapper, ) cmap_plot._window = window = mock.Mock(spec=AbstractWindow) #when cmap_plot.color_mapper.updated = True # Then window.redraw.assert_called_once_with() if __name__ == "__main__": unittest.main() chaco-4.5.0/chaco/tests/test_colormapped_scatterplot.py0000644000076600000240000000536212426462425024153 0ustar jrocherstaff00000000000000import unittest from unittest2 import skip from numpy import alltrue, arange from enable.compiled_path import CompiledPath # Chaco imports from chaco.api import (ArrayDataSource, ColormappedScatterPlot, DataRange1D, LinearMapper, PlotGraphicsContext, jet) class TestColormappedScatterplot(unittest.TestCase): def setUp(self): self.index = ArrayDataSource(arange(10)) self.value = ArrayDataSource(arange(10)) self.color_data = ArrayDataSource(arange(10)) self.size_data = arange(10) self.index_range = DataRange1D() self.index_range.add(self.index) self.index_mapper = LinearMapper(range=self.index_range) self.value_range = DataRange1D() self.value_range.add(self.value) self.value_mapper = LinearMapper(range=self.value_range) self.color_range = DataRange1D() self.color_range.add(self.color_data) self.color_mapper = jet(self.color_range) self.scatterplot = ColormappedScatterPlot( index=self.index, value=self.value, index_mapper=self.index_mapper, value_mapper=self.value_mapper, color_data=self.color_data, marker_size=self.size_data, color_mapper=self.color_mapper, ) self.scatterplot.outer_bounds = [50, 50] self.gc = PlotGraphicsContext((50, 50)) def test_scatter_render(self): """ Coverage test to check basic case works """ self.gc.render_component(self.scatterplot) actual = self.gc.bmp_array[:, :, :] self.assertFalse(alltrue(actual == 255)) def test_scatter_circle(self): """ Coverage test to check circles work """ self.scatterplot.marker = 'circle' self.gc.render_component(self.scatterplot) actual = self.gc.bmp_array[:, :, :] self.assertFalse(alltrue(actual == 255)) @skip def test_scatter_custom(self): """ Coverage test to check custom markers work... XXX ...which apparently they currently don't. See #232. """ # build path path = CompiledPath() path.begin_path() path.move_to(-5, -5) path.line_to(-5, 5) path.line_to(5, 5) path.line_to(5, -5) path.line_to(-5, -5) self.scatterplot.marker = 'custom' self.scatterplot.custom_symbol = path self.gc.render_component(self.scatterplot) actual = self.gc.bmp_array[:, :, :] self.assertFalse(alltrue(actual == 255)) def test_colormap_updated(self): """ If colormapper updated then we need to redraw """ self.color_mapper.updated = True self.assertFalse(self.scatterplot.draw_valid) if __name__ == "__main__": unittest.main() chaco-4.5.0/chaco/text_box_overlay.py0000644000076600000240000001177612426452312020416 0ustar jrocherstaff00000000000000""" Defines the TextBoxOverlay class. """ from __future__ import with_statement # Enthought library imports from enable.api import ColorTrait from kiva.trait_defs.kiva_font_trait import KivaFont from traits.api import Any, Enum, Int, Str, Float, Trait, Bool # Local, relative imports from abstract_overlay import AbstractOverlay from label import Label class TextBoxOverlay(AbstractOverlay): """ Draws a box with text in it. """ #### Configuration traits ################################################# # The text to display in the box. text = Str # The font to use for the text. font = KivaFont("modern 12") # The background color for the box (overrides AbstractOverlay). bgcolor = ColorTrait("transparent") # The alpha value to apply to **bgcolor** alpha = Trait(1.0, None, Float) # The color of the outside box. border_color = ColorTrait("dodgerblue") # The color of the text. text_color = ColorTrait("black") # The thickness of box border. border_size = Int(1) # The border visibility. Defaults to true to duplicate previous behavior. border_visible = Bool(True) # Number of pixels of padding around the text within the box. padding = Int(5) # The maximum width of the displayed text. This affects the width of the # text only, not the text box, which includes margins around the text and # `padding`. # A `max_text_width` of 0.0 means that the width will not be restricted. max_text_width = Float(0.0) # Alignment of the text in the box: # # * "ur": upper right # * "ul": upper left # * "ll": lower left # * "lr": lower right align = Enum("ur", "ul", "ll", "lr") # This allows subclasses to specify an alternate position for the root # of the text box. Must be a sequence of length 2. alternate_position = Any #### Public 'AbstractOverlay' interface ################################### def overlay(self, component, gc, view_bounds=None, mode="normal"): """ Draws the box overlaid on another component. Overrides AbstractOverlay. """ if not self.visible: return # draw the label on a transparent box. This allows us to draw # different shapes and put the text inside it without the label # filling a rectangle on top of it label = Label(text=self.text, font=self.font, bgcolor="transparent", color=self.text_color, max_width=self.max_text_width, margin=5) width, height = label.get_width_height(gc) valign, halign = self.align if self.alternate_position: x, y = self.alternate_position if valign == "u": y += self.padding else: y -= self.padding + height if halign == "r": x += self.padding else: x -= self.padding + width else: if valign == "u": y = component.y2 - self.padding - height else: y = component.y + self.padding if halign == "r": x = component.x2 - self.padding - width else: x = component.x + self.padding # attempt to get the box entirely within the component x_min, y_min, x_max, y_max = (component.x, component.y, component.x + component.width, component.y + component.height) if x + width > x_max: x = max(x_min, x_max - width) if y + height > y_max: y = max(y_min, y_max - height) elif y < y_min: y = y_min # apply the alpha channel color = self.bgcolor_ if self.bgcolor != "transparent": if self.alpha: color = list(self.bgcolor_) if len(color) == 4: color[3] = self.alpha else: color += [self.alpha] with gc: gc.translate_ctm(x, y) gc.set_line_width(self.border_size) gc.set_stroke_color(self.border_color_) gc.set_fill_color(color) if self.border_visible: # draw a rounded rectangle. x = y = 0 end_radius = 8.0 gc.begin_path() gc.move_to(x + end_radius, y) gc.arc_to(x + width, y, x + width, y + end_radius, end_radius) gc.arc_to(x + width, y + height, x + width - end_radius, y + height, end_radius) gc.arc_to(x, y + height, x, y, end_radius) gc.arc_to(x, y, x + width + end_radius, y, end_radius) gc.draw_path() label.draw(gc) chaco-4.5.0/chaco/ticks.py0000644000076600000240000004476512426452312016142 0ustar jrocherstaff00000000000000#------------------------------------------------------------------------------- # # # Written by: David C. Morrill (based on similar routines written by Eric Jones) # # Date: 2007-05-01 # # (c) Copyright 2002-7 by Enthought, Inc. # #------------------------------------------------------------------------------- """ Tick generator classes and helper functions for calculating axis tick-related values (i.e., bounds and intervals). """ # Major library imports from numpy import arange, argsort, array, ceil, concatenate, equal, finfo, \ float64, floor, linspace, log10, minimum, ndarray, newaxis, \ putmask, shape # Enthought library imports from traits.api import HasTraits, Any class AbstractTickGenerator(HasTraits): """ Abstract class for tick generators. """ def get_ticks(self, data_low, data_high, bounds_low, bounds_high, interval, use_endpoints=False, scale='linear'): """ Returns a list of ticks points in data space. Parameters ---------- data_low, data_high : float The actual minimum and maximum of index values of the entire dataset. bounds_low, bounds_high : "auto", "fit", float The range for which ticks should be generated. interval : "auto", float If the value is a positive number, it specifies the length of the tick interval; a negative integer specifies the number of tick intervals; 'auto' specifies that the number and length of the tick intervals are automatically calculated, based on the range of the axis. use_endpoints : Boolean If True, the lower and upper bounds of the data are used as the lower and upper end points of the axis. If False, the end points might not fall exactly on the bounds. scale : 'linear' or 'log' The type of scale the ticks are for. Returns ------- tick_list : array of floats Where ticks are to be placed. Example ------- If the range of x-values in a line plot span from -15.0 to +15.0, but the plot is currently displaying only the region from 3.1 to 6.83, and the user wants the interval to be automatically computed to be some nice value, then call get_ticks() thusly:: get_ticks(-15.0, 15.0, 3.1, 6.83, "auto") A reasonable return value in this case would be:: [3.5, 4.0, 4.5, 5.0, 5.5, 6.0, 6.5] """ raise NotImplementedError class DefaultTickGenerator(AbstractTickGenerator): """ An implementation of AbstractTickGenerator that simply uses the auto_ticks() and log_auto_ticks() functions. """ def get_ticks(self, data_low, data_high, bounds_low, bounds_high, interval, use_endpoints=False, scale='linear'): if scale == 'linear': return array(auto_ticks(data_low, data_high, bounds_low, bounds_high, interval, use_endpoints=False), float64) elif scale == 'log': return array(log_auto_ticks(data_low, data_high, bounds_low, bounds_high, interval, use_endpoints=False), float64) class ShowAllTickGenerator(AbstractTickGenerator): """ Uses the abstract interface, but returns all "positions" instead of decimating the ticks. You must provide a sequence of values as a *positions* keyword argument to the constructor. """ # A sequence of positions for ticks. positions = Any def get_ticks(self, data_low, data_high, bounds_low, bounds_high, interval, use_endpoints=False, scale='linear'): """ Returns an array based on **positions**. """ # ignore all the high, low, etc. data and just return every position return array(self.positions, float64) #------------------------------------------------------------------------------- # Code imported from plt/plot_utility.py: #------------------------------------------------------------------------------- def auto_ticks ( data_low, data_high, bound_low, bound_high, tick_interval, use_endpoints = True): """ Finds locations for axis tick marks. Calculates the locations for tick marks on an axis. The *bound_low*, *bound_high*, and *tick_interval* parameters specify how the axis end points and tick interval are calculated. Parameters ---------- data_low, data_high : number The minimum and maximum values of the data along this axis. If any of the bound settings are 'auto' or 'fit', the axis traits are calculated automatically from these values. bound_low, bound_high : 'auto', 'fit', or a number. The lower and upper bounds of the axis. If the value is a number, that value is used for the corresponding end point. If the value is 'auto', then the end point is calculated automatically. If the value is 'fit', then the axis bound is set to the corresponding *data_low* or *data_high* value. tick_interval : can be 'auto' or a number If the value is a positive number, it specifies the length of the tick interval; a negative integer specifies the number of tick intervals; 'auto' specifies that the number and length of the tick intervals are automatically calculated, based on the range of the axis. use_endpoints : Boolean If True, the lower and upper bounds of the data are used as the lower and upper end points of the axis. If False, the end points might not fall exactly on the bounds. Returns ------- An array of tick mark locations. The first and last tick entries are the axis end points. """ is_auto_low = (bound_low == 'auto') is_auto_high = (bound_high == 'auto') if isinstance(bound_low, basestring): lower = data_low else: lower = float( bound_low ) if isinstance(bound_high, basestring): upper = data_high else: upper = float( bound_high ) if (tick_interval == 'auto') or (tick_interval == 0.0): rng = abs( upper - lower ) if rng == 0.0: tick_interval = 0.5 lower = data_low - 0.5 upper = data_high + 0.5 elif is_base2( rng ) and is_base2( upper ) and rng > 4: if rng == 2: tick_interval = 1 elif rng == 4: tick_interval = 4 else: tick_interval = rng / 4 # maybe we want it 8? else: tick_interval = auto_interval( lower, upper ) elif tick_interval < 0: intervals = -tick_interval tick_interval = tick_intervals( lower, upper, intervals ) if is_auto_low and is_auto_high: is_auto_low = is_auto_high = False lower = tick_interval * floor( lower / tick_interval ) while ((abs( lower ) >= tick_interval) and ((lower + tick_interval * (intervals - 1)) >= upper)): lower -= tick_interval upper = lower + tick_interval * intervals # If the lower or upper bound are set to 'auto', # calculate them based on the newly chosen tick_interval: if is_auto_low or is_auto_high: delta = 0.01 * tick_interval * (data_low == data_high) auto_lower, auto_upper = auto_bounds( data_low - delta, data_high + delta, tick_interval ) if is_auto_low: lower = auto_lower if is_auto_high: upper = auto_upper # Compute the range of ticks values: start = floor( lower / tick_interval ) * tick_interval end = floor( upper / tick_interval ) * tick_interval # If we return the same value for the upper bound and lower bound, the # layout code will not be able to lay out the tick marks (divide by zero). if start == end: lower = start = start - tick_interval upper = end = start - tick_interval if upper > end: end += tick_interval ticks = arange( start, end + (tick_interval / 2.0), tick_interval ) if len( ticks ) < 2: ticks = array( ( ( lower - lower * 1.0e-7 ), lower ) ) if (not is_auto_low) and use_endpoints: ticks[0] = lower if (not is_auto_high) and use_endpoints: ticks[-1] = upper return [tick for tick in ticks if tick >= bound_low and tick <= bound_high] #-------------------------------------------------------------------------------- # Determine if a number is a power of 2: #-------------------------------------------------------------------------------- def is_base2 ( range ): """ Returns True if *range* is a positive base-2 number (2, 4, 8, 16, ...). """ if range <= 0.0: return False else: lg = log2( range ) return ((lg == floor( lg )) and (lg > 0.0)) #-------------------------------------------------------------------------------- # Compute n log 2: #-------------------------------------------------------------------------------- def log2 ( num ): """ Returns the base 2 logarithm of a number (or array). """ # !! 1e-16 is here to prevent errors when log is 0 if num == 0.0: num += 1.0e-16 elif type( num ) is ndarray: putmask( num, equal( num, 0.0), 1.0e-16 ) return log10( num ) / log10( 2 ) #-------------------------------------------------------------------------------- # Compute the best tick interval for a specified data range: #-------------------------------------------------------------------------------- def heckbert_interval(data_low, data_high, numticks=8): """ Returns a "nice" range and interval for a given data range and a preferred number of ticks. From Paul Heckbert's algorithm in Graphics Gems. """ range = _nice(data_high - data_low) d = _nice(range / (numticks-1), round=True) graphmin = floor(data_low / d) * d graphmax = ceil(data_high / d) * d #nfrac = max(-floor(log10(d)), 0) return graphmin, graphmax, d def _nice(x, round=False): """ if round is False, then use ceil(range) """ expv = floor(log10(x)) f = x / pow(10, expv) if round: if f < 1.5: nf = 1.0 elif f < 3.0: nf = 2.0 elif f < 7.0: nf = 5.0; else: nf = 10.0 else: if f <= 1.0: nf = 1.0 elif f <= 2.0: nf = 2.0 elif f <= 5.0: nf = 5.0 else: nf = 10.0 return nf * pow(10, expv) def auto_interval ( data_low, data_high ): """ Calculates the tick interval for a range. The boundaries for the data to be plotted on the axis are:: data_bounds = (data_low,data_high) The function chooses the number of tick marks, which can be between 3 and 9 marks (including end points), and chooses tick intervals at 1, 2, 2.5, 5, 10, 20, ... Returns ------- interval : float tick mark interval for axis """ range = float( data_high ) - float( data_low ) # We'll choose from between 2 and 8 tick marks. # Preference is given to more ticks: # Note reverse order and see kludge below... divisions = arange( 8.0, 2.0, -1.0 ) # ( 7, 6, ..., 3 ) # Calculate the intervals for the divisions: candidate_intervals = range / divisions # Get magnitudes and mantissas for each candidate: magnitudes = 10.0 ** floor( log10( candidate_intervals ) ) mantissas = candidate_intervals / magnitudes # List of "pleasing" intervals between ticks on graph. # Only the first magnitude are listed, higher mags others are inferred: magic_intervals = array( ( 1.0, 2.0, 2.5, 5.0, 10.0 ) ) # Calculate the absolute differences between the candidates # (with magnitude removed) and the magic intervals: differences = abs( magic_intervals[:,newaxis] - mantissas ) # Find the division and magic interval combo that produce the # smallest differences: # KLUDGE: 'argsort' doesn't preserve the order of equal values, # so we subtract a small, index dependent amount from each difference # to force correct ordering. sh = shape( differences ) small = 2.2e-16 * arange( sh[1] ) * arange( sh[0] )[:,newaxis] small = small[::-1,::-1] #reverse the order differences = differences - small # ? Numeric should allow keyword "axis" ? comment out for now #best_mantissa = minimum.reduce(differences,axis=0) #best_magic = minimum.reduce(differences,axis=-1) best_mantissa = minimum.reduce( differences, 0 ) best_magic = minimum.reduce( differences, -1 ) magic_index = argsort( best_magic )[0] mantissa_index = argsort( best_mantissa )[0] # The best interval is the magic_interval multiplied by the magnitude # of the best mantissa: interval = magic_intervals[ magic_index ] magnitude = magnitudes[ mantissa_index ] result = interval * magnitude if result == 0.0: result = finfo(float).eps return result #-------------------------------------------------------------------------------- # Compute the best tick interval length to achieve a specified number of tick # intervals: #-------------------------------------------------------------------------------- def tick_intervals ( data_low, data_high, intervals ): """ Computes the best tick interval length to achieve a specified number of tick intervals. Parameters ---------- data_low, data_high : number The minimum and maximum values of the data along this axis. If any of the bound settings are 'auto' or 'fit', the axis traits are calculated automatically from these values. intervals : number The desired number of intervals Returns ------- Returns a float indicating the tick interval length. """ range = float( data_high - data_low ) if range == 0.0: range = 1.0 interval = range / intervals factor = 10.0 ** floor( log10( interval ) ) interval /= factor if interval < 2.0: interval = 2.0 index = 0 elif interval < 2.5: interval = 2.5 index = 1 elif interval < 5.0: interval = 5.0 index = 2 else: interval = 10.0 index = 3 while True: result = interval * factor if ((floor( data_low / result ) * result) + (intervals * result) >= data_high): return result index = (index + 1) % 4 interval *= ( 2.0, 1.25, 2.0, 2.0 )[ index ] def log_auto_ticks(data_low, data_high, bound_low, bound_high, tick_interval, use_endpoints = True): """Like auto_ticks(), but for log scales.""" tick_goal = 15 magic_numbers = [1, 2, 5] explicit_ticks = False if data_low<=0.0: return [] if tick_interval != 'auto': if tick_interval < 0: tick_goal = -tick_interval else: magic_numbers = [tick_interval] explicit_ticks = True if data_low>data_high: data_low, data_high = data_high, data_low log_low = log10(data_low) log_high = log10(data_high) log_interval = log_high-log_low if log_interval < 1.0: # If less than a factor of 10 separates the data, just use the normal # linear approach return auto_ticks(data_low, data_high, bound_low, bound_high, tick_interval, use_endpoints = False) elif log_interval < (tick_goal+1)/2 or explicit_ticks: # If there's enough space, try to put lines at the magic number multipliers # inside each power of ten # Try each interval to see how many ticks we get for interval in magic_numbers: ticklist = [] for exp in range(int(floor(log_low)), int(ceil(log_high))): for multiplier in linspace(interval, 10.0, round(10.0/interval), endpoint=1): tick = 10**exp*multiplier if tick >= data_low and tick <= data_high: ticklist.append(tick) if len(ticklist) self.max_zoom_out_factor or \ (orig_bounds / new_bounds) > self.max_zoom_in_factor: return True return False #------------------------------------------------------------------------ # Utility methods for computing axes, coordinates, etc. #------------------------------------------------------------------------ def _get_mapper(self): """ Returns the mapper for the component associated with this tool. The zoom tool really only cares about this, so subclasses can easily customize SimpleZoom to work with all sorts of components just by overriding this method. """ if self.component is not None: return getattr(self.component, self.axis + "_mapper") else: return None def _get_axis_coord(self, event, axis="index"): """ Returns the coordinate of the event along the axis of interest to the tool (or along the orthogonal axis, if axis="value"). """ event_pos = (event.x, event.y) if axis == "index": return event_pos[ self._determine_axis() ] else: return event_pos[ 1 - self._determine_axis() ] def _determine_axis(self): """ Determines whether the index of the coordinate along the axis of interest is the first or second element of an (x,y) coordinate tuple. """ if self.axis == "index": if self.component.orientation == "h": return 0 else: return 1 else: # self.axis == "value" if self.component.orientation == "h": return 1 else: return 0 def _map_coordinate_box(self, start, end): """ Given start and end points in screen space, returns corresponding low and high points in data space. """ low = [0,0] high = [0,0] for axis_index, mapper in [(0, self.component.x_mapper), \ (1, self.component.y_mapper)]: low_val = mapper.map_data(start[axis_index]) high_val = mapper.map_data(end[axis_index]) if low_val > high_val: low_val, high_val = high_val, low_val low[axis_index] = low_val high[axis_index] = high_val return low, high chaco-4.5.0/chaco/tools/better_selecting_zoom.py0000644000076600000240000004263212426462254022550 0ustar jrocherstaff00000000000000from __future__ import with_statement import numpy from chaco.abstract_overlay import AbstractOverlay from enable.api import ColorTrait, KeySpec from traits.api import Bool, Enum, Trait, Int, Float, Tuple, Instance, Property from traits.util.deprecated import deprecated from better_zoom import BetterZoom from tool_states import SelectedZoomState class BetterSelectingZoom(AbstractOverlay, BetterZoom): """ Zooming tool which allows the user to draw a box which defines the desired region to zoom in to """ # The selection mode: # # range: # Select a range across a single index or value axis. # box: # Perform a "box" selection on two axes. tool_mode = Enum("box", "range") # Is the tool always "on"? If True, left-clicking always initiates # a zoom operation; if False, the user must press a key to enter zoom mode. always_on = Bool(False) # Defines a meta-key, that works with always_on to set the zoom mode. This # is useful when the zoom tool is used in conjunction with the pan tool. always_on_modifier = Enum('control', 'shift', 'control', 'alt') # The mouse button that initiates the drag. If "None", then the tool # will not respond to drag. (It can still respond to mousewheel events.) drag_button = Enum("left", "right", None) # The minimum amount of screen space the user must select in order for # the tool to actually take effect. minimum_screen_delta = Int(10) #------------------------------------------------------------------------- # deprecated interaction controls, used for API compatability with # SimpleZoom #------------------------------------------------------------------------- # Conversion ratio from wheel steps to zoom factors. wheel_zoom_step = Property(Float, depends_on='zoom_factor') # The key press to enter zoom mode, if **always_on** is False. Has no effect # if **always_on** is True. enter_zoom_key = Instance(KeySpec, args=("z",)) # The key press to leave zoom mode, if **always_on** is False. Has no effect # if **always_on** is True. exit_zoom_key = Instance(KeySpec, args=("z",)) # Disable the tool after the zoom is completed? disable_on_complete = Property() #------------------------------------------------------------------------- # Appearance properties (for Box mode) #------------------------------------------------------------------------- # The pointer to use when drawing a zoom box. pointer = "magnifier" # The color of the selection box. color = ColorTrait("lightskyblue") # The alpha value to apply to **color** when filling in the selection # region. Because it is almost certainly useless to have an opaque zoom # rectangle, but it's also extremely useful to be able to use the normal # named colors from Enable, this attribute allows the specification of a # separate alpha value that replaces the alpha value of **color** at draw # time. alpha = Trait(0.4, None, Float) # The color of the outside selection rectangle. border_color = ColorTrait("dodgerblue") # The thickness of selection rectangle border. border_size = Int(1) # The possible event states of this zoom tool. event_state = Enum("normal", "selecting", "pre_selecting") # The (x,y) screen point where the mouse went down. _screen_start = Trait(None, None, Tuple) # The (x,,y) screen point of the last seen mouse move event. _screen_end = Trait(None, None, Tuple) # If **always_on** is False, this attribute indicates whether the tool # is currently enabled. _enabled = Bool(False) #------------------------------------------------------------------------- # Private traits #------------------------------------------------------------------------- # the original numerical screen ranges _orig_low_setting = Tuple _orig_high_setting = Tuple def __init__(self, component=None, *args, **kw): # Since this class uses multiple inheritance (eek!), lets be # explicit about the order of the parent class constructors AbstractOverlay.__init__(self, component, *args, **kw) BetterZoom.__init__(self, component, *args, **kw) # Store the original range settings x_range = self._get_x_mapper().range y_range = self._get_y_mapper().range self._orig_low_setting = (x_range.low_setting, y_range.low_setting) self._orig_high_setting = (x_range.high_setting, y_range.high_setting) def reset(self, event=None): """ Resets the tool to normal state, with no start or end position. """ self.event_state = "normal" self._screen_start = None self._screen_end = None #-------------------------------------------------------------------------- # BetterZoom interface #-------------------------------------------------------------------------- def normal_key_pressed(self, event): """ Handles a key being pressed when the tool is in the 'normal' state. """ if not self.always_on: if self.enter_zoom_key.match(event) and not self._enabled: self.event_state = 'pre_selecting' event.window.set_pointer(self.pointer) event.window.set_mouse_owner(self, event.net_transform()) self._enabled = True event.handled = True elif self.exit_zoom_key.match(event) and self._enabled: self.state = 'normal' self._end_select(event) event.handled = True if not event.handled: super(BetterSelectingZoom, self).normal_key_pressed(event) def normal_left_down(self, event): """ Handles the left mouse button being pressed while the tool is in the 'normal' state. If the tool is enabled or always on, it starts selecting. """ if self._is_enabling_event(event): self._start_select(event) event.handled = True return def normal_right_down(self, event): """ Handles the right mouse button being pressed while the tool is in the 'normal' state. If the tool is enabled or always on, it starts selecting. """ if self._is_enabling_event(event): self._start_select(event) event.handled = True return def pre_selecting_left_down(self, event): """ The user pressed the key to turn on the zoom mode, now handle the click to start the select mode """ self._start_select(event) event.handled = True def pre_selecting_key_pressed(self, event): """ Handle key presses, specifically the exit zoom key """ if self.exit_zoom_key.match(event) and self._enabled: self._end_selecting(event) def selecting_key_pressed(self, event): """ Handle key presses, specifically the exit zoom key """ if self.exit_zoom_key.match(event) and self._enabled: self._end_selecting(event) def selecting_mouse_move(self, event): """ Handles the mouse moving when the tool is in the 'selecting' state. The selection is extended to the current mouse position. """ # Take into account when we update the current endpoint, but only # if we are in box select mode. The way we handle aspect ratio # is to find the largest rectangle of the specified aspect which # will fit within the rectangle defined by the start coords and # the current mouse position. if self.tool_mode == "box" and self.aspect_ratio is not None: x1, y1 = self._screen_start x2, y2 = event.x, event.y if (y2 - y1) == 0: x2 = x1 y2 = y1 else: width = abs(x2 - x1) height = abs(y2 - y1) drawn_aspect = width / height if drawn_aspect > self.aspect_ratio: # Drawn box is wider, so use its height to compute the # restricted width x2 = x1 + height * self.aspect_ratio * (1 if x2 > x1 else -1) else: # Drawn box is taller, so use its width to compute the # restricted height y2 = y1 + width / self.aspect_ratio * (1 if y2 > y1 else -1) self._screen_end = (x2, y2) else: self._screen_end = (event.x, event.y) self.component.request_redraw() event.handled = True return def selecting_left_up(self, event): """ Handles the left mouse button being released when the tool is in the 'selecting' state. Finishes selecting and does the zoom. """ if self.drag_button in ("left", None): self._end_select(event) return def selecting_right_up(self, event): """ Handles the right mouse button being released when the tool is in the 'selecting' state. Finishes selecting and does the zoom. """ if self.drag_button == "right": self._end_select(event) return def selecting_mouse_leave(self, event): """ Handles the mouse leaving the plot when the tool is in the 'selecting' state. Ends the selection operation without zooming. """ self._end_selecting(event) return #-------------------------------------------------------------------------- # AbstractOverlay interface #-------------------------------------------------------------------------- def overlay(self, component, gc, view_bounds=None, mode="normal"): """ Draws this component overlaid on another component. Overrides AbstractOverlay. """ if self.event_state == "selecting": if self.tool_mode == "range": self._overlay_range(component, gc) else: self._overlay_box(component, gc) return #-------------------------------------------------------------------------- # private interface #-------------------------------------------------------------------------- @deprecated def _get_disable_on_complete(self): return True @deprecated def _set_disable_on_complete(self, value): return @deprecated def _get_wheel_zoom_step(self): return self.zoom_factor - 1.0 @deprecated def _set_wheel_zoom_step(self, value): self.zoom_factor = value + 1.0 def _is_enabling_event(self, event): if self.always_on: enabled = True else: if self.always_on_modifier == 'shift': enabled = event.shift_down elif self.always_on_modifier == 'control': enabled = event.control_down elif self.always_on_modifier == 'alt': enabled = event.alt_down if enabled: if event.right_down and self.drag_button == 'right': return True if event.left_down and self.drag_button == 'left': return True return False def _start_select(self, event): """ Starts selecting the zoom region """ if self.component.active_tool in (None, self): self.component.active_tool = self else: self._enabled = False self._screen_start = (event.x, event.y) self._screen_end = None self.event_state = "selecting" event.window.set_pointer(self.pointer) event.window.set_mouse_owner(self, event.net_transform()) self.selecting_mouse_move(event) return def _end_select(self, event): """ Ends selection of the zoom region, adds the new zoom range to the zoom stack, and does the zoom. """ self._screen_end = (event.x, event.y) start = numpy.array(self._screen_start) end = numpy.array(self._screen_end) if sum(abs(end - start)) < self.minimum_screen_delta: self._end_selecting(event) event.handled = True return low, high = self._map_coordinate_box(self._screen_start, self._screen_end) x_range = self._get_x_mapper().range y_range = self._get_y_mapper().range prev = (x_range.low, x_range.high, y_range.low, y_range.high) if self.tool_mode == 'range': axis = self._determine_axis() if axis == 1: # vertical next = (x_range.low, x_range.high, low[1], high[1]) else: # horizontal next = (low[0], high[0], y_range.low, y_range.high) else: next = (low[0], high[0], low[1], high[1]) zoom_state = SelectedZoomState(prev, next) zoom_state.apply(self) self._append_state(zoom_state) self._end_selecting(event) event.handled = True return def _end_selecting(self, event=None): """ Ends selection of zoom region, without zooming. """ self.reset() self._enabled = False if self.component.active_tool == self: self.component.active_tool = None if event and event.window: event.window.set_pointer("arrow") self.component.request_redraw() if event and event.window.mouse_owner == self: event.window.set_mouse_owner(None) return def _overlay_box(self, component, gc): """ Draws the overlay as a box. """ if self._screen_start and self._screen_end: with gc: gc.set_antialias(0) gc.set_line_width(self.border_size) gc.set_stroke_color(self.border_color_) gc.clip_to_rect(component.x, component.y, component.width, component.height) x, y = self._screen_start x2, y2 = self._screen_end rect = (x, y, x2-x+1, y2-y+1) if self.color != "transparent": if self.alpha: color = list(self.color_) if len(color) == 4: color[3] = self.alpha else: color += [self.alpha] else: color = self.color_ gc.set_fill_color(color) gc.draw_rect(rect) else: gc.rect(*rect) gc.stroke_path() return def _overlay_range(self, component, gc): """ Draws the overlay as a range. """ axis_ndx = self._determine_axis() lower_left = [0,0] upper_right = [0,0] lower_left[axis_ndx] = self._screen_start[axis_ndx] lower_left[1-axis_ndx] = self.component.position[1-axis_ndx] upper_right[axis_ndx] = self._screen_end[axis_ndx] - self._screen_start[axis_ndx] upper_right[1-axis_ndx] = self.component.bounds[1-axis_ndx] with gc: gc.set_antialias(0) gc.set_alpha(self.alpha) gc.set_fill_color(self.color_) gc.set_stroke_color(self.border_color_) gc.clip_to_rect(component.x, component.y, component.width, component.height) gc.draw_rect((lower_left[0], lower_left[1], upper_right[0], upper_right[1])) return def _determine_axis(self): """ Determines whether the index of the coordinate along the axis of interest is the first or second element of an (x,y) coordinate tuple. """ if self.axis == "index": if self.component.orientation == "h": return 0 else: return 1 else: if self.component.orientation == "h": return 1 else: return 0 def _map_coordinate_box(self, start, end): """ Given start and end points in screen space, returns corresponding low and high points in data space. """ low = [0,0] high = [0,0] for axis_index, mapper in [(0, self.component.x_mapper), \ (1, self.component.y_mapper)]: # Ignore missing axis mappers (ColorBar instances only have one). if not mapper: continue low_val = mapper.map_data(start[axis_index]) high_val = mapper.map_data(end[axis_index]) if low_val > high_val: low_val, high_val = high_val, low_val low[axis_index] = low_val high[axis_index] = high_val return low, high def _reset_range_settings(self): """ Reset the range settings to their original values """ x_range = self._get_x_mapper().range y_range = self._get_y_mapper().range x_range.low_setting, y_range.low_setting = self._orig_low_setting x_range.high_setting, y_range.high_setting = self._orig_high_setting #-------------------------------------------------------------------------- # overloaded #-------------------------------------------------------------------------- def _prev_state_pressed(self): super(BetterSelectingZoom, self)._prev_state_pressed() # Reset the range settings if self._history_index == 0: self._reset_range_settings() def _reset_state_pressed(self): super(BetterSelectingZoom, self)._reset_state_pressed() # Reset the range settings self._reset_range_settings() chaco-4.5.0/chaco/tools/better_zoom.py0000644000076600000240000003105412426452312020501 0ustar jrocherstaff00000000000000import numpy from chaco.grid_mapper import GridMapper from enable.api import BaseTool, KeySpec from traits.api import Enum, Float, Instance, Bool, HasTraits, List from tool_history_mixin import ToolHistoryMixin from tool_states import ZoomState, PanState, GroupedToolState, ToolState class BetterZoom(BaseTool, ToolHistoryMixin): # Keys to zoom in/out zoom_in_key = Instance(KeySpec, args=("+",), kw={'ignore': ['shift']}) zoom_out_key = Instance(KeySpec, args=("-",)) # Keys to zoom in/out in x direction only zoom_in_x_key = Instance(KeySpec, args=("Right", "shift")) zoom_out_x_key = Instance(KeySpec, args=("Left", "shift")) # Keys to zoom in/out in y direction only zoom_in_y_key = Instance(KeySpec, args=("Up", "shift")) zoom_out_y_key = Instance(KeySpec, args=("Down", "shift")) # Key to go to the previous state in the history. prev_state_key = Instance(KeySpec, args=("z", "control")) # Key to go to the next state in the history. next_state_key = Instance(KeySpec, args=("y", "control")) # Enable the mousewheel for zooming? enable_wheel = Bool(True) # if the mouse pointer should be used to control the center # of the zoom action zoom_to_mouse = Bool(True) # if the mouse pointer should be used to control the center # of the zoom action even for key events based zoom keys_zoom_to_mouse = Bool(True) # The axis to which the selection made by this tool is perpendicular. This # only applies in 'range' mode. axis = Enum("both", "index", "value") # The maximum ratio between the original data space bounds and the zoomed-in # data space bounds. If No limit is desired, set to inf x_max_zoom_factor = Float(1e5) y_max_zoom_factor = Float(1e5) # The maximum ratio between the zoomed-out data space bounds and the original # bounds. If No limit is desired, set to -inf x_min_zoom_factor = Float(1e-5) y_min_zoom_factor = Float(1e-5) # The amount to zoom in by. The zoom out will be inversely proportional zoom_factor = Float(2.0) # The zoom factor on each axis _index_factor = Float(1.0) _value_factor = Float(1.0) # inherited from ToolHistoryMixin, but requires instances of ZoomState _history = List(ToolState, [ZoomState((1.0, 1.0), (1.0, 1.0))]) def _do_zoom(self, new_index_factor, new_value_factor): if self.zoom_to_mouse: location = self.position x_map = self._get_x_mapper() y_map = self._get_y_mapper() cx = (x_map.range.high + x_map.range.low)/2 if self._index_factor == new_index_factor: nextx = cx else: x = x_map.map_data(location[0]) nextx = x + (cx - x)*(self._index_factor/new_index_factor) cy = (y_map.range.high + y_map.range.low)/2 if self._value_factor == new_value_factor: nexty = cy else: y = y_map.map_data(location[1]) nexty = y + (cy - y)*(self._value_factor/new_value_factor) pan_state = PanState((cx,cy), (nextx, nexty)) zoom_state = ZoomState((self._index_factor, self._value_factor), (new_index_factor, new_value_factor)) states = GroupedToolState([pan_state, zoom_state]) states.apply(self) self._append_state(states) else: zoom_state = ZoomState((self._index_factor, self._value_factor), (new_index_factor, new_value_factor)) zoom_state.apply(self) self._append_state(zoom_state) #-------------------------------------------------------------------------- # public interface #-------------------------------------------------------------------------- def zoom_in(self, factor=0): if factor == 0: factor = self.zoom_factor new_index_factor = self._index_factor * factor new_value_factor = self._value_factor * factor if self.axis == 'value': new_index_factor = self._index_factor elif self.axis == 'index': new_value_factor = self._value_factor if self.component.orientation == 'h': if self._zoom_limit_reached(new_index_factor, 'x'): return if self._zoom_limit_reached(new_value_factor, 'y'): return else: if self._zoom_limit_reached(new_index_factor, 'y'): return if self._zoom_limit_reached(new_value_factor, 'x'): return self._do_zoom(new_index_factor, new_value_factor) def zoom_out(self, factor=0): if factor == 0: factor = self.zoom_factor new_index_factor = self._index_factor / factor new_value_factor = self._value_factor / factor if self.axis == 'value': new_index_factor = self._index_factor elif self.axis == 'index': new_value_factor = self._value_factor if self.component.orientation == 'h': if self._zoom_limit_reached(new_index_factor, 'x'): return if self._zoom_limit_reached(new_value_factor, 'y'): return else: if self._zoom_limit_reached(new_index_factor, 'y'): return if self._zoom_limit_reached(new_value_factor, 'x'): return self._do_zoom(new_index_factor, new_value_factor) def zoom_in_x(self, factor=0): if factor == 0: factor = self.zoom_factor if self.component.orientation == 'h': new_index_factor = self._index_factor * factor new_value_factor = self._value_factor if self._zoom_limit_reached(new_index_factor, 'x'): return else: new_index_factor = self._index_factor new_value_factor = self._value_factor * factor if self._zoom_limit_reached(new_value_factor, 'x'): return self._do_zoom(new_index_factor, new_value_factor) def zoom_out_x(self, factor=0): if factor == 0: factor = self.zoom_factor if self.component.orientation == 'h': new_index_factor = self._index_factor / factor new_value_factor = self._value_factor if self._zoom_limit_reached(new_index_factor, 'x'): return else: new_index_factor = self._index_factor new_value_factor = self._value_factor / factor if self._zoom_limit_reached(new_value_factor, 'x'): return self._do_zoom(new_index_factor, new_value_factor) def zoom_in_y(self, factor=0): if factor == 0: factor = self.zoom_factor if self.component.orientation == 'v': new_index_factor = self._index_factor * factor new_value_factor = self._value_factor if self._zoom_limit_reached(new_index_factor, 'y'): return else: new_index_factor = self._index_factor new_value_factor = self._value_factor * factor if self._zoom_limit_reached(new_value_factor, 'y'): return self._do_zoom(new_index_factor, new_value_factor) def zoom_out_y(self, factor=0): if factor == 0: factor = self.zoom_factor if self.component.orientation == 'v': new_index_factor = self._index_factor / factor new_value_factor = self._value_factor if self._zoom_limit_reached(new_index_factor, 'y'): return else: new_index_factor = self._index_factor new_value_factor = self._value_factor / factor if self._zoom_limit_reached(new_value_factor, 'y'): return self._do_zoom(new_index_factor, new_value_factor) #-------------------------------------------------------------------------- # BaseTool interface #-------------------------------------------------------------------------- def normal_key_pressed(self, event): """ Handles a key being pressed when the tool is in the 'normal' state. """ if not self.keys_zoom_to_mouse: self.position = self._center_screen() if self.zoom_in_key.match(event): self.zoom_in() event.handled = True elif self.zoom_out_key.match(event): self.zoom_out() event.handled = True elif self.zoom_in_x_key.match(event): self.zoom_in_x(self.zoom_factor) event.handled = True elif self.zoom_out_x_key.match(event): self.zoom_out_x(self.zoom_factor) event.handled = True elif self.zoom_in_y_key.match(event): self.zoom_in_y(self.zoom_factor) event.handled = True elif self.zoom_out_y_key.match(event): self.zoom_out_y(self.zoom_factor) event.handled = True ToolHistoryMixin.normal_key_pressed(self, event) return def normal_mouse_wheel(self, event): if not self.enable_wheel: return if event.mouse_wheel != 0: if event.mouse_wheel > 0: self.zoom_in() else: self.zoom_out() event.handled = True def normal_mouse_move(self, event): self.position = (event.x, event.y) def normal_mouse_enter(self, event): """ Try to set the focus to the window when the mouse enters, otherwise the keypress events will not be triggered. """ if self.component._window is not None: self.component._window._set_focus() #-------------------------------------------------------------------------- # private interface #-------------------------------------------------------------------------- def _center_screen(self): return self.component.bounds[0]/2, self.component.bounds[1]/2 def _zoom_limit_reached(self, factor, xy_axis): """ Returns True if the new low and high exceed the maximum zoom limits """ if xy_axis == 'x': if factor <= self.x_max_zoom_factor and factor >= self.x_min_zoom_factor: return False return True else: if factor <= self.y_max_zoom_factor and factor >= self.y_min_zoom_factor: return False return True def _zoom_in_mapper(self, mapper, factor): high = mapper.range.high low = mapper.range.low range = high-low center = (low + high)/2.0 new_range = range/factor mapper.range.high = center + new_range/2 mapper.range.low = center - new_range/2 def _get_x_mapper(self): if isinstance(self.component.index_mapper, GridMapper): if self.component.orientation == "h": return self.component.index_mapper._xmapper return self.component.index_mapper._ymapper else: if self.component.orientation == "h": return self.component.index_mapper return self.component.value_mapper def _get_y_mapper(self): if isinstance(self.component.index_mapper, GridMapper): if self.component.orientation == "h": return self.component.index_mapper._ymapper return self.component.index_mapper._xmapper else: if self.component.orientation == "h": return self.component.value_mapper return self.component.index_mapper #-------------------------------------------------------------------------- # ToolHistoryMixin interface #-------------------------------------------------------------------------- def _next_state_pressed(self): """ Called when the tool needs to advance to the next state in the stack. The **_history_index** will have already been set to the index corresponding to the next state. """ self._current_state().apply(self) def _prev_state_pressed(self): """ Called when the tool needs to advance to the previous state in the stack. The **_history_index** will have already been set to the index corresponding to the previous state. """ self._history[self._history_index+1].revert(self) def _reset_state_pressed(self): """ Called when the tool needs to reset its history. The history index will have already been set to 0. """ for state in self._history[::-1]: state.revert(self) self._history = [] chaco-4.5.0/chaco/tools/broadcaster.py0000644000076600000240000000355312426452312020444 0ustar jrocherstaff00000000000000""" Defines the BroadcasterTool class. """ from enable.api import BaseTool from traits.api import Dict, List class BroadcasterTool(BaseTool): """ A simple tool that keeps a list of other tools, and broadcasts events it receives to all of the tools. """ # The tools to which this tool broadcasts events. tools = List # Mapping from tools to transforms, for tools that can be mouse owners. # (See enable.AbstractWindow.) mouse_owners = Dict def dispatch(self, event, suffix): """ Dispatches a mouse event based on the current event state. Overrides BaseTool. """ handled = False # keeps track of whether any tool handled this event if event.window.mouse_owner == self: tools = self.mouse_owners.keys() mouse_owned = True else: tools = self.tools mouse_owned = False for tool in tools: if mouse_owned: event.window.set_mouse_owner(tool, self.mouse_owners[tool]) tool.dispatch(event, suffix) if event.handled: handled = True event.handled = False if mouse_owned and event.window.mouse_owner is None: # The tool owned the mouse before handling the previous event, # and now doesn't, so remove it from the list of mouse_owners del self.mouse_owners[tool] elif not mouse_owned and event.window.mouse_owner == tool: # The tool is a new mouse owner self.mouse_owners[tool] = event.window.mouse_owner_transform if len(self.mouse_owners) == 0: if event.window.mouse_owner == self: event.window.set_mouse_owner(None) else: event.window.set_mouse_owner(self, event.net_transform()) event.handled = handled chaco-4.5.0/chaco/tools/cursor_tool.py0000644000076600000240000002345012426452312020523 0ustar jrocherstaff00000000000000""" Defines some chaco tools to provide draggable cursor functionality For XY-plots, the cursor tool requires the index_sort flag to be set to either 'ascending' or 'descending'. TODO: - add some visual feedback to the user when a cursor is "grabbed" (e.g. highlight the cursor line) - update cursor position to the "selections" metadata on the owning plot component """ from __future__ import with_statement # Major library imports import numpy # Enthought library imports from enable.tools.drag_tool import DragTool from traits.api import Int, Property, cached_property, Float,\ Bool, Instance, Tuple, Disallow # Chaco imports from chaco.scatter_markers import CircleMarker from chaco.base_xy_plot import BaseXYPlot from chaco.base_2d_plot import Base2DPlot from line_inspector import LineInspector def CursorTool(component, *args, **kwds): """ Factory function returning either a CursorTool1D or CursorTool2D instance depending on whether the provided plot component is an XY-plot or a 2D plot. """ if isinstance(component, BaseXYPlot): return CursorTool1D(component, *args, **kwds) elif isinstance(component, Base2DPlot): return CursorTool2D(component, *args, **kwds) else: raise NotImplementedError("cursors available for BaseXYPlot and Base2DPlot only") class BaseCursorTool(LineInspector, DragTool): """ Abstract base class for CursorTool objects """ #if true, draw a small circle at the cursor/line intersection show_marker = Bool(True) #the radius of the marker in pixels marker_size = Float(3.0) #the marker object. this should probably be private marker = Instance(CircleMarker, ()) #pick threshold, in screen units (pixels) threshold = Float(5.0) #The current index-value of the cursor. Over-ridden in subclasses current_index = Disallow #The current position of the cursor in data units current_position = Property(depends_on=['current_index']) #Stuff from line_inspector which is not required axis = Disallow inspect_mode = Disallow is_interactive = Disallow is_listener = Disallow write_metadata = Disallow metadata_name = Disallow def _draw_marker(self, gc, sx, sy): """ Ruthlessly hijacked from the scatterplot.py class. This design is silly; the choice of rendering path should be encapsulated within the GC. """ if sx < self.component.x or sx > self.component.x2 or \ sy < self.component.y or sy > self.component.y2: return marker = self.marker marker_size = self.marker_size points = [(sx,sy)] with gc: gc.set_fill_color(self.color_) gc.begin_path() # This is the fastest method - use one of the kiva built-in markers if hasattr(gc, "draw_marker_at_points") \ and (gc.draw_marker_at_points(points, marker_size, marker.kiva_marker) != 0): pass # The second fastest method - draw the path into a compiled path, then # draw the compiled path at each point elif hasattr(gc, 'draw_path_at_points'): #if debug: # import pdb; pdb.set_trace() path = gc.get_empty_path() marker.add_to_path(path, marker_size) mode = marker.draw_mode if not marker.antialias: gc.set_antialias(False) gc.draw_path_at_points(points, path, mode) # Neither of the fast functions worked, so use the brute-force, manual way else: if not marker.antialias: gc.set_antialias(False) for sx,sy in points: with gc: gc.translate_ctm(sx, sy) # Kiva GCs have a path-drawing interface marker.add_to_path(gc, marker_size) gc.draw_path(marker.draw_mode) return def normal_mouse_move(self, event): """ Handles the mouse being moved. """ return def normal_mouse_leave(self, event): """ Handles the mouse leaving the plot. """ return class CursorTool1D(BaseCursorTool): """ This tools provides a draggable cursor bound to a XY plot component instance. Note, be sure to select an drag_button which does not conflict with other tools (e.g. the PanTool). """ #The current index-value of the cursor current_index = Int(0) #if true, draws a line parallel to the index-axis #through the cursor intersection point show_value_line = Bool(True) def _current_index_changed(self): self.component.request_redraw() @cached_property def _get_current_position(self): plot = self.component ndx = self.current_index x = plot.index.get_data()[ndx] y = plot.value.get_data()[ndx] return x,y def _set_current_position(self, traitname, args): plot = self.component ndx = plot.index.reverse_map(args[0]) if ndx is not None: self.current_index = ndx def draw(self, gc, view_bounds=None): """ Draws this tool on a graphics context. Overrides LineInspector, BaseTool. """ # We draw at different points depending on whether or not we are # interactive. If both listener and interactive are true, then the # selection metadata on the plot component takes precendence. plot = self.component if plot is None: return sx, sy = plot.map_screen(self.current_position) orientation = plot.orientation if orientation == "h" and sx is not None: self._draw_vertical_line(gc, sx) elif sy is not None: self._draw_horizontal_line(gc, sy) if self.show_marker: self._draw_marker(gc, sx, sy) if self.show_value_line: if orientation == "h" and sy is not None: self._draw_horizontal_line(gc, sy) elif sx is not None: self._draw_vertical_line(gc, sx) def is_draggable(self, x, y): plot = self.component if plot is not None: orientation = plot.orientation sx, sy = plot.map_screen(self.current_position) if orientation=='h' and numpy.abs(sx-x) <= self.threshold: return True elif orientation=='v' and numpy.abs(sy-y) <= self.threshold: return True return False def dragging(self, event): x,y = event.x, event.y plot = self.component ndx = plot.map_index((x, y), threshold=0.0, index_only=True) if ndx is None: return self.current_index = ndx plot.request_redraw() class CursorTool2D(BaseCursorTool): _dragV = Bool(False) _dragH = Bool(False) current_index = Tuple(0,0) def _current_index_changed(self): self.component.request_redraw() @cached_property def _get_current_position(self): plot = self.component ndx, ndy = self.current_index xdata, ydata = plot.index.get_data() x = xdata.get_data()[ndx] y = ydata.get_data()[ndy] return x,y def _set_current_position(self, traitname, args): plot = self.component xds, yds = plot.index.get_data() ndx = xds.reverse_map(args[0]) ndy = yds.reverse_map(args[1]) if ndx is not None and ndy is not None: self.current_index = ndx, ndy def is_draggable(self, x, y): plot = self.component if plot is not None: orientation = plot.orientation sx, sy = plot.map_screen([self.current_position])[0] self._dragV = self._dragH = False if orientation=='h': if numpy.abs(sx-x) <= self.threshold: self._dragH = True if numpy.abs(sy-y) <= self.threshold: self._dragV = True else: if numpy.abs(sx-x) <= self.threshold: self._dragV = True if numpy.abs(sy-y) <= self.threshold: self._dragH = True return self._dragV or self._dragH return False def draw(self, gc, view_bounds=None): """ Draws this tool on a graphics context. Overrides LineInspector, BaseTool. """ # We draw at different points depending on whether or not we are # interactive. If both listener and interactive are true, then the # selection metadata on the plot component takes precendence. plot = self.component if plot is None: return sx, sy = plot.map_screen([self.current_position])[0] orientation = plot.orientation if orientation == "h": if sx is not None: self._draw_vertical_line(gc, sx) if sy is not None: self._draw_horizontal_line(gc, sy) else: if sx is not None: self._draw_horizontal_line(gc, sx) if sy is not None: self._draw_vertical_line(gc, sy) if self.show_marker and sx is not None and sy is not None: self._draw_marker(gc, sx, sy) def dragging(self, event): x,y = event.x, event.y plot = self.component ndx = plot.map_index((x, y), threshold=0.0, index_only=True) if ndx is None: return newx, newy = self.current_index if self._dragH and ndx[0] is not None: newx = ndx[0] if self._dragV and ndx[1] is not None: newy = ndx[1] self.current_index = newx, newy plot.request_redraw() chaco-4.5.0/chaco/tools/data_label_tool.py0000644000076600000240000000653512426452312021263 0ustar jrocherstaff00000000000000""" Defines the DataLabelTool class. """ # Major library imports from numpy import array, asarray, argmin, sqrt # Enthought library imports from traits.api import Any, Bool, Enum from enable.tools.drag_tool import DragTool class DataLabelTool(DragTool): """ A tool for dragging a data label. Attach this tool to a DataLabel object by setting the tool's **component** to the DataLabel. """ # The mouse button that initiates the drag. drag_button = Enum("left", "right") # Use the root of the label's arrow (if any) as the closest corner of the # label? auto_arrow_root = Bool(True) # The original position of the label with respect to the data point. _original_offset = Any # This is used in the auto_arrow_root = 'corners' case. _corner_names = ("bottom left", "bottom right", "top right", "top left", "top center", "bottom center", "left center", "right center") def is_draggable(self, x, y): """ Returns whether the (x,y) position is in a region that is OK to drag. Overrides DragTool. """ if self.component: label = self.component return (x >= label.x and x <= label.x2 and \ y >= label.y and y <= label.y2) else: return False def drag_start(self, event): """ Called when the drag operation starts. Implements DragTool. """ if self.component: label = self.component pointx, pointy = label.component.map_screen(label.data_point) self._original_offset = (label.x - pointx, label.y - pointy) event.window.set_mouse_owner(self, event.net_transform()) event.handled = True return def dragging(self, event): """ This method is called for every mouse_move event that the tool receives while the user is dragging the mouse. Implements DragTool. Moves and redraws the label. """ if self.component: label = self.component dx = int(event.x - self.mouse_down_position[0]) dy = int(event.y - self.mouse_down_position[1]) label.label_position = (self._original_offset[0] + dx, self._original_offset[1] + dy) if self.auto_arrow_root: # Determine which corner is closest to the point p = asarray(label.component.map_screen(label.data_point)) x, y = label.position x2 = label.x2 y2 = label.y2 xmid = (x+x2)/2 ymid = (y+y2)/2 anchors = array(((x, y), (x2, y), (x2, y2), (x, y2), (xmid, y2), (xmid, y), (x, ymid), (x2, ymid))) diff = anchors - p closest = argmin((diff ** 2).sum(axis=-1)) label.arrow_root = self._corner_names[closest] event.handled = True label.request_redraw() return def drag_end(self, event): """ Called when a mouse event causes the drag operation to end. Implements DragTool. """ if self.component: if event.window.mouse_owner == self: event.window.set_mouse_owner(None) event.handled = True self.component.request_redraw() return chaco-4.5.0/chaco/tools/dataprinter.py0000644000076600000240000000237212426452312020466 0ustar jrocherstaff00000000000000""" Defines the DataPrinter tool class. """ # Enthought library imports from traits.api import Str from enable.api import BaseTool # Chaco imports from chaco.api import BaseXYPlot class DataPrinter(BaseTool): """ Simple listener tool that prints the (x,y) data space position of the point under the cursor. """ # This tool is a listener, and does not display anything (overrides BaseTool). visible = False # Turn off drawing, because the tool prints to stdout. draw_mode = "none" # The string to format the (x,y) value in data space. format = Str("(%.3f, %.3f)") def normal_mouse_move(self, event): """ Handles the mouse being moved in the 'normal' state. Prints the data space position of the current mouse position. """ plot = self.component if plot is not None: if isinstance(plot, BaseXYPlot): ndx = plot.map_index((event.x, event.y), index_only = True) x = plot.index.get_data()[ndx] y = plot.value.get_data()[ndx] print self.format % (x,y) else: print "dataprinter: don't know how to handle plots of type", print plot.__class__.__name__ return # EOF chaco-4.5.0/chaco/tools/drag_tool.py0000644000076600000240000000051412426452312020117 0ustar jrocherstaff00000000000000 # This is a duplicate of the enable.DragTool class and will be # removed in future versions. Please import DragTool directly # from enable. import warnings warnings.warn("chaco.tools.DragTool has been removed." "Use enable.tools.DragTool instead.", DeprecationWarning) from enable.tools.drag_tool import DragTool chaco-4.5.0/chaco/tools/drag_zoom.py0000644000076600000240000001070512426452312020131 0ustar jrocherstaff00000000000000""" Defines a the DragZoom tool class """ # Enthought library imports from enable.tools.drag_tool import DragTool from traits.api import Bool, Enum, Float, Tuple # Chaco imports from better_zoom import BetterZoom class DragZoom(DragTool, BetterZoom): """ A zoom tool that zooms continuously with a mouse drag movement, instead of using a zoom box or range. By default, the tool maintains aspect ratio and zooms the plot's X and Y axes by the same amount as the user drags up and down. (In this default configuration, the horizontal position of the drag motion has no effect.) By setting **maintain_aspect_ratio** to False, this tool will separably zoom the X and Y axis ranges by the (possibly different) horizontal and vertical drag motions. This is similar to the drag zoom interaction in Matplotlib. By setting single_axis to True, dragging will only zoom in the axis specified by the axis attribute. By setting restrict_domain to True, the zoom will be limited to the domain of the axis mappers. """ # The mouse button that initiates the drag drag_button = Enum("left", "right", "middle") # Scaling factor on the zoom "speed". A speed of 1.0 implies a zoom rate of # 5% for every 10 pixels. speed = Float(1.0) # Whether or not to preserve the aspect ratio of X to Y while zooming in. # (See class docstring for more info.) maintain_aspect_ratio = Bool(True) # The pointer to use when we're in the act of zooming drag_pointer = "magnifier" # Whether or not to zoom in one axis only single_axis = Bool(False) # Whether to restrict zoom to the domain of the mappers restrict_domain = Bool(False) zoom_to_mouse = Bool(False) #------------------------------------------------------------------------------ # Private traits #------------------------------------------------------------------------------ # (x,y) of the point where the mouse button was pressed. _original_xy = Tuple() # Data coordinates of **_original_xy**. This may be either (index,value) # or (value,index) depending on the component's orientation. _original_data = Tuple() # A tuple of ((x,y), (x2,y2)) of the original, unzoomed screen bounds _orig_screen_bounds = Tuple() # The x and y positions of the previous mouse event. The zoom rate is # based on the percentage change in position between the previous position # and the current mouse position, possibly in both axes. _prev_x = Float() _prev_y = Float() def __init__(self, component=None, *args, **kw): super(DragZoom, self).__init__(component, *args, **kw) c = component if c is not None: self._orig_screen_bounds = ((c.x, c.y), (c.x2, c.y2)) def dragging(self, event): # Compute the zoom amount based on the pixel difference between # the previous mouse event and the current one. if self.maintain_aspect_ratio: zoom_x = zoom_y = self._calc_zoom(self._prev_y, event.y) else: zoom_x = self._calc_zoom(self._prev_x, event.x) zoom_y = self._calc_zoom(self._prev_y, event.y) # invert the zoom so scrolling up zooms in zoom_x = 1.0/zoom_x zoom_y = 1.0/zoom_y self.zoom_in_x(zoom_x) self.zoom_in_y(zoom_y) return def drag_start(self, event, capture_mouse=True): self._original_xy = (event.x, event.y) c = self.component self._orig_screen_bounds = ((c.x,c.y), (c.x2,c.y2)) self._original_data = (c.x_mapper.map_data(event.x), c.y_mapper.map_data(event.y)) self._prev_x = event.x self._prev_y = event.y if capture_mouse: event.window.set_pointer(self.drag_pointer) event.window.set_mouse_owner(self, event.net_transform()) event.handled = True return def drag_end(self, event): event.window.set_pointer("arrow") if event.window.mouse_owner == self: event.window.set_mouse_owner(None) event.handled = True return def _calc_zoom(self, original, clicked): """ Returns the amount to scale the range based on the original cursor position and a new, updated position. """ # We express the built-in zoom scaling as 0.05/10 to indicate a scaling # of 5% every 10 pixels, per the docstring for the 'speed' trait. return 1.0 - self.speed * (clicked - original) * (0.05/10) chaco-4.5.0/chaco/tools/draw_points_tool.py0000644000076600000240000000330012426452312021527 0ustar jrocherstaff00000000000000""" Defines the DrawPointsTool class. """ # Major library imports from numpy import array, float64, hstack # Enthought library imports from traits.api import Instance, Bool from enable.api import BaseTool # Chaco import from chaco.api import ArrayDataSource class DrawPointsTool(BaseTool): """ A tool that draws points onto a rectangular plot as they are clicked. """ # A data source for the x-dimension of the drawn points. xdata = Instance(ArrayDataSource) # A data source for the y-dimension of the drawn points. ydata = Instance(ArrayDataSource) # Is this the active tool? activated = Bool(True) #It would be nice to set the pointer to a cross def __init__(self, **kwtraits): BaseTool.__init__(self, **kwtraits) self.xdata = self.component.value1 self.ydata = self.component.value2 return def normal_left_down(self, event): """ Handles the left mouse button being clicked when the tool is in the 'normal' state. Maps the event position into data space, adds the point to the points for this tool, and redraws. """ x,y = event.x, event.y data_x, data_y = self.component.map_data((x,y)) self._append_data(self.xdata, data_x) self._append_data(self.ydata, data_y) self.component.request_redraw() return def _activate(self): self.activated = True return def _deactivate(self): self.activated = False return def _append_data(self, datasource, data): olddata = array(datasource.get_data(), float64) newdata = hstack((olddata, data)) datasource.set_data(newdata) return #EOF chaco-4.5.0/chaco/tools/highlight_tool.py0000644000076600000240000000634412426452312021160 0ustar jrocherstaff00000000000000""" Defines the HighlightTool class. """ # Major library imports from numpy import ones # Enthought library imports from traits.api import Enum, Float, Str from enable.api import BaseTool # Chaco imports from chaco.api import BasePlotContainer class HighlightTool(BaseTool): """ A tool that enables the user to select a plot to be highlighted on the graph by clicking on it. """ # The name of the data source metadata which controls selections. metadata_name = Str('selections') # The mouse button that initiates the selection. drag_button = Enum("left", "right") # Threshold distance for hit-testing. threshold = Float(20.0) #--------------------------------------------------------------------- # Inherited BaseTool traits #--------------------------------------------------------------------- # This tool is not drawn. Overrides BaseTool. draw_mode = "none" # This tool is not visible. Overrides BaseTool. visible = False def normal_left_down(self, event): """ Handles the left mouse button being pressed. If the left mouse button initiates the selection, this method does so. """ if self.drag_button == "left": self._highlight(event) return def normal_right_down(self, event): """ Handles the right mouse button being pressed. If the right mouse button initiates the selection, this method does so. """ if self.drag_button == "right": self._highlight(event) return def _highlight(self, event): if isinstance(self.component, BasePlotContainer): event.offset_xy(self.component.x, self.component.y) closest_plot = self._find_curve(self.component.components, event) if closest_plot: index = closest_plot.index index.metadata[self.metadata_name] = ones(len(index.get_data()), dtype=bool) closest_plot.request_redraw() else: # If we are attached to a plot container, then we can deselect # all of the plots in the container for p in self.component.components: if self.metadata_name in p.index.metadata: del p.index.metadata[self.metadata_name] p.request_redraw() event.pop() elif hasattr(self.component, "hittest"): hit_point = self.component.hittest((event.x, event.y), self.threshold) index = self.component.index if hit_point is not None: index.metadata[self.metadata_name] = ones(len(index.get_data()), dtype=bool) self.component.request_redraw() elif self.metadata_name in index.metadata: del index.metadata[self.metadata_name] self.component.request_redraw() event.handled = True return def _find_curve(self, plots, event): # need to change to use distance - not just return first plot within threshold for p in plots: if hasattr(p, "hittest"): cpoint = p.hittest((event.x,event.y), self.threshold) if cpoint: return p return None #EOF chaco-4.5.0/chaco/tools/image_inspector_tool.py0000644000076600000240000001232312426452312022353 0ustar jrocherstaff00000000000000""" Defines the ImageInspectorTool, ImageInspectorOverlay, and ImageInspectorColorbarOverlay classes. """ # Enthought library imports from enable.api import BaseTool, KeySpec from traits.api import Any, Bool, Enum, Event, Tuple # Chaco imports from chaco.api import AbstractOverlay, ImagePlot, TextBoxOverlay class ImageInspectorTool(BaseTool): """ A tool that captures the color and underlying values of an image plot. """ # This event fires whenever the mouse moves over a new image point. # Its value is a dict with a key "color_value", and possibly a key # "data_value" if the plot is a color-mapped image plot. new_value = Event # Indicates whether overlays listening to this tool should be visible. visible = Bool(True) # Stores the last mouse position. This can be used by overlays to # position themselves around the mouse. last_mouse_position = Tuple # This key will show and hide any ImageInspectorOverlays associated # with this tool. inspector_key = KeySpec('p') # Stores the value of self.visible when the mouse leaves the tool, # so that it can be restored when the mouse enters again. _old_visible = Enum(None, True, False) #Trait(None, Bool(True)) def normal_key_pressed(self, event): if self.inspector_key.match(event): self.visible = not self.visible event.handled = True def normal_mouse_leave(self, event): if self._old_visible is None: self._old_visible = self.visible self.visible = False def normal_mouse_enter(self, event): if self._old_visible is not None: self.visible = self._old_visible self._old_visible = None def normal_mouse_move(self, event): """ Handles the mouse being moved. Fires the **new_value** event with the data (if any) from the event's position. """ plot = self.component if plot is not None: if isinstance(plot, ImagePlot): ndx = plot.map_index((event.x, event.y)) if ndx == (None, None): self.new_value = None return x_index, y_index = ndx image_data = plot.value if hasattr(plot, "_cached_mapped_image") and \ plot._cached_mapped_image is not None: self.new_value = \ dict(indices=ndx, data_value=image_data.data[y_index, x_index], color_value=plot._cached_mapped_image[y_index, x_index]) else: self.new_value = \ dict(indices=ndx, color_value=image_data.data[y_index, x_index]) self.last_mouse_position = (event.x, event.y) return class ImageInspectorOverlay(TextBoxOverlay): """ An overlay that displays a box containing values from an ImageInspectorTool instance. """ # An instance of ImageInspectorTool; this overlay listens to the tool # for changes, and updates its displayed text accordingly. image_inspector = Any # Anchor the text to the mouse? (If False, then the text is in one of the # corners.) Use the **align** trait to determine which corner. tooltip_mode = Bool(False) # The default state of the overlay is invisible (overrides PlotComponent). visible = False # Whether the overlay should auto-hide and auto-show based on the # tool's location, or whether it should be forced to be hidden or visible. visibility = Enum("auto", True, False) def _image_inspector_changed(self, old, new): if old: old.on_trait_event(self._new_value_updated, 'new_value', remove=True) old.on_trait_change(self._tool_visible_changed, "visible", remove=True) if new: new.on_trait_event(self._new_value_updated, 'new_value') new.on_trait_change(self._tool_visible_changed, "visible") self._tool_visible_changed() def _new_value_updated(self, event): if event is None: self.text = "" if self.visibility == "auto": self.visible = False return elif self.visibility == "auto": self.visible = True if self.tooltip_mode: self.alternate_position = self.image_inspector.last_mouse_position else: self.alternate_position = None d = event newstring = "" if 'indices' in d: newstring += '(%d, %d)' % d['indices'] + '\n' if 'color_value' in d: newstring += "(%d, %d, %d)" % tuple(map(int,d['color_value'][:3])) + "\n" if 'data_value' in d: newstring += str(d['data_value']) self.text = newstring self.component.request_redraw() def _visible_changed(self): self.component.request_redraw() def _tool_visible_changed(self): self.visibility = self.image_inspector.visible if self.visibility != "auto": self.visible = self.visibility class ImageInspectorColorbarOverlay(AbstractOverlay): pass chaco-4.5.0/chaco/tools/lasso_selection.py0000644000076600000240000002775312426452312021351 0ustar jrocherstaff00000000000000""" Defines the LassoSelection controller class. """ # Major library imports import numpy from numpy import array, empty, sometrue, transpose, vstack, zeros # Enthought library imports from traits.api import Any, Array, Enum, Event, Bool, Instance, \ Property, Str, Trait, List from kiva.agg import points_in_polygon # Chaco imports from chaco.api import AbstractController, AbstractDataSource, \ BaseXYPlot, Base2DPlot class LassoSelection(AbstractController): """ A controller that represents the interaction of "lassoing" a set of points. "Lassoing" means drawing an arbitrary selection region around the points by dragging the mouse along the outline of the region. """ # An Nx2 array of points in data space representing all selected points. dataspace_points = Property(Array) # A list of all the selection polygons. disjoint_selections = Property(List) # Fires whenever **dataspace_points** changes, necessitating a redraw of the # selection region. updated = Event # Fires when the selection mask changes. selection_changed = Event # Fires when the user release the mouse button and finalizes the selection. selection_completed = Event # If True, the selection mask is updated as the mouse moves, rather # than only at the beginning and end of the selection operation. incremental_select = Bool(False) # The selection mode of the lasso pointer: "include", "exclude" or # "invert" points from the selection. The "include" and "exclude" # settings essentially invert the selection mask. The "invert" setting # differs from "exclude" in that "invert" inverses the selection of all # points the the lasso'ed polygon, while "exclude" operates only on # points included in a previous selection. selection_mode = Enum("include", "exclude", "invert") # The data source that the mask of selected points is attached to. Note # that the indices in this data source must match the indices of the data # in the plot. selection_datasource = Instance(AbstractDataSource) # The name of the metadata on the datasource that we will write # the selection mask to metadata_name = Str("selection") # Mapping from screen space to data space. By default, it is just # self.component. plot = Property # The button which this tool responds to drag_button = Enum("left", "right") # The possible event states of this selection tool (overrides # enable.Interactor). # # normal: # Nothing has been selected, and the user is not dragging the mouse. # selecting: # The user is dragging the mouse and is actively changing the # selection region. event_state = Enum('normal', 'selecting') #---------------------------------------------------------------------- # Private Traits #---------------------------------------------------------------------- # The PlotComponent associated with this tool. _plot = Trait(None, Any) # To support multiple selections, a list of cached selections and the # active selection are maintained. A single list is not used because the # active selection is re-created every time a new point is added via # the vstack function. _active_selection = Array _previous_selections = List(Array) #---------------------------------------------------------------------- # Properties #---------------------------------------------------------------------- def _get_dataspace_points(self): """ Returns a complete list of all selected points. This property exists for backwards compatibility, as the disjoint_selections property is almost always the preferred method of accessingselected points """ composite = empty((0,2)) for region in self.disjoint_selections: if len(region) > 0: composite = vstack((composite, region)) return composite def _get_disjoint_selections(self): """ Returns a list of all disjoint selections composed of the previous selections and the active selection """ if len(self._active_selection) == 0: return self._previous_selections else: return self._previous_selections + [self._active_selection] #---------------------------------------------------------------------- # Event Handlers #---------------------------------------------------------------------- def normal_left_down(self, event): if self.drag_button == "left": return self.normal_mouse_down(event) def normal_right_down(self, event): if self.drag_button == "right": return self.normal_mouse_down(event) def normal_mouse_down(self, event): """ Handles the left mouse button being pressed while the tool is in the 'normal' state. Puts the tool into 'selecting' mode, and starts defining the selection. """ # We may want to generalize this for the n-dimensional case... self._active_selection = empty((0,2), dtype=numpy.bool) if self.selection_datasource is not None: self.selection_datasource.metadata[self.metadata_name] = zeros(len(self.selection_datasource.get_data()), dtype=numpy.bool) self.selection_mode = "include" self.event_state = 'selecting' self.selecting_mouse_move(event) if (not event.shift_down) and (not event.control_down): self._previous_selections = [] else: if event.control_down: self.selection_mode = "exclude" else: self.selection_mode = "include" self.trait_property_changed("disjoint_selections", [], self.disjoint_selections) return def selecting_left_up(self, event): if self.drag_button == "left": return self.selecting_mouse_up(event) def selecting_right_up(self, event): if self.drag_button == "right": return self.selecting_mouse_up(event) def selecting_mouse_up(self, event): """ Handles the mouse button coming up in the 'selecting' state. Completes the selection and switches to the 'normal' state. """ self.event_state = 'normal' self.selection_completed = True self._update_selection() self._previous_selections.append(self._active_selection) self._active_selection = empty((0,2), dtype=numpy.bool) return def selecting_mouse_move(self, event): """ Handles the mouse moving when the tool is in the 'selecting' state. The selection is extended to the current mouse position. """ # Translate the event's location to be relative to this container xform = self.component.get_event_transform(event) event.push_transform(xform, caller=self) new_point = self._map_data(array((event.x, event.y))) self._active_selection = vstack((self._active_selection, array((new_point,)))) self.updated = True if self.incremental_select: self._update_selection() # Report None for the previous selections self.trait_property_changed("disjoint_selections", None) return def selecting_mouse_leave(self, event): """ Handles the mouse leaving the plot when the tool is in the 'selecting' state. Ends the selection operation. """ # Treat this as if it were a selecting_mouse_up event return self.selecting_mouse_up(event) def normal_key_pressed(self, event): """ Handles the user pressing a key in the 'normal' state. If the user presses the Escape key, the tool is reset. """ if event.character == "Esc": self._reset() elif event.character == 'a' and event.control_down: self._reset() self._select_all() elif event.character == 'i' and event.control_down: self.selecting_mouse_up(None) self.selection_mode = 'invert' self._select_all() return #---------------------------------------------------------------------- # Protected Methods #---------------------------------------------------------------------- def _dataspace_points_default(self): return empty((0,2), dtype=numpy.bool) def _reset(self): """ Resets the selection """ self.event_state='normal' self._active_selection = empty((0,2), dtype=numpy.bool) self._previous_selections = [] self._update_selection() def _select_all(self): """ Selects all points in the plot. This is done by making a rectangle using the corners of the plot, which is simple but effective. A much cooler, but more time-intensive solution would be to make a selection polygon representing the convex hull. """ points = [self._map_data(array((self.plot.x, self.plot.y2))), self._map_data(array((self.plot.x2, self.plot.y2))), self._map_data(array((self.plot.x2, self.plot.y))), self._map_data(array((self.plot.x, self.plot.y)))] self._active_selection = numpy.array(points) self._update_selection() def _update_selection(self): """ Sets the selection datasource's metadata to a mask of all the points selected """ if self.selection_datasource is None: return selected_mask = zeros(self.selection_datasource._data.shape, dtype=numpy.bool) data = self._get_data() # Compose the selection mask from the cached selections first, then # the active selection, taking into account the selection mode only # for the active selection for selection in self._previous_selections: selected_mask |= (points_in_polygon(data, selection, False)) if self.selection_mode == 'exclude': selected_mask |= (points_in_polygon(data, self._active_selection, False)) selected_mask = 1 - selected_mask elif self.selection_mode == 'invert': selected_mask = -1 * (selected_mask -points_in_polygon(data, self._active_selection, False)) else: selected_mask |= (points_in_polygon(data, self._active_selection, False)) if sometrue(selected_mask != self.selection_datasource.metadata[self.metadata_name]): self.selection_datasource.metadata[self.metadata_name] = selected_mask self.selection_changed = True return def _map_screen(self, points): """ Maps a point in data space to a point in screen space on the plot. Normally this method is a pass-through, but it may do more in specialized plots. """ return self.plot.map_screen(points)[:,:2] def _map_data(self, point): """ Maps a point in screen space to data space. Normally this method is a pass-through, but for plots that have more data than just (x,y), proper transformations need to happen here. """ if isinstance(self.plot, Base2DPlot): # Base2DPlot.map_data takes an array of points, for some reason return self.plot.map_data([point])[0] elif isinstance(self.plot, BaseXYPlot): return self.plot.map_data(point, all_values=True)[:2] else: raise RuntimeError("LassoSelection only supports BaseXY and Base2D plots") def _get_data(self): """ Returns the datapoints in the plot, as an Nx2 array of (x,y). """ return transpose(array((self.plot.index.get_data(), self.plot.value.get_data()))) #------------------------------------------------------------------------ # Property getter/setters #------------------------------------------------------------------------ def _get_plot(self): if self._plot is not None: return self._plot else: return self.component def _set_plot(self, val): self._plot = val return chaco-4.5.0/chaco/tools/legend_highlighter.py0000644000076600000240000000732012426452312021763 0ustar jrocherstaff00000000000000import operator # ETS imports from chaco.tools.api import LegendTool from traits.api import List, Float def get_hit_plots(legend, event): if legend is None or not legend.is_in(event.x, event.y): return [] try: # FIXME: The size of the legend is not being computed correctly, so # always look at the front of the label where we know we'll get a hit. label = legend.get_label_at(legend.x + 20, event.y) except: raise label = None if label is None: return [] try: ndx = legend._cached_labels.index(label) label_name = legend._cached_label_names[ndx] renderers = legend.plots[label_name] return renderers except (ValueError, KeyError): return [] class LegendHighlighter(LegendTool): """ A tool for legends that allows clicking on the legend to show or hide certain plots. """ #: Which mousebutton to use to move the legend drag_button = "right" #: What to divide the alpha value by when plot is not selected dim_factor = Float(3.0) #: How much to scale the line when it is selected or deselected line_scale = Float(2.0) # The currently selected renderers _selected_renderers = List def normal_left_down(self, event): if not self.component.is_in(event.x, event.y): return plots = get_hit_plots(self.component, event) if len(plots) > 0: plot = plots[0] if event.shift_down: # User in multi-select mode by using [shift] key. if plot in self._selected_renderers: self._selected_renderers.remove(plot) else: self._selected_renderers.append(plot) else: # User in single-select mode. add_plot = plot not in self._selected_renderers self._selected_renderers = [] if add_plot: self._selected_renderers.append(plot) if self._selected_renderers: self._set_states(self.component.plots) else: self._reset_selects(self.component.plots) plot.request_redraw() event.handled = True def _reset_selects(self, plots): """ Set all renderers to their default values. """ for plot in reduce(operator.add, plots.values()): if not hasattr(plot, '_orig_alpha'): plot._orig_alpha = plot.alpha plot._orig_line_width = plot.line_width plot.alpha = plot._orig_alpha plot.line_width = plot._orig_line_width return def _set_states(self, plots): """ Decorates a plot to indicate it is selected """ for plot in reduce(operator.add, plots.values()): if not hasattr(plot, '_orig_alpha'): # FIXME: These attributes should be put into the class def. plot._orig_alpha = plot.alpha plot._orig_line_width = plot.line_width if plot in self._selected_renderers: plot.line_width = plot._orig_line_width * self.line_scale plot.alpha = plot._orig_alpha else: plot.alpha = plot._orig_alpha / self.dim_factor plot.line_width = plot._orig_line_width / self.line_scale # Move the selected renderers to the front if len(self._selected_renderers) > 0: container = self._selected_renderers[0].container components = container.components[:] for renderer in self._selected_renderers: components.remove(renderer) components += self._selected_renderers container._components = components chaco-4.5.0/chaco/tools/legend_tool.py0000644000076600000240000000747112426452312020451 0ustar jrocherstaff00000000000000""" Defines the LegendTool class. """ # Enthought library imports from traits.api import Bool, Enum from enable.tools.drag_tool import DragTool class LegendTool(DragTool): """ A tool for interacting with legends. Attach this tool to a legend by setting the tool's **component** to the legend. """ # The mouse button that initiates the drag. drag_button = Enum("left", "right") # Whether to change the legend's **align** property in accord with # the quadrant into which it is dropped. auto_align = Bool(True) def is_draggable(self, x, y): """ Returns whether the (x,y) position is in a region that is OK to drag. Overrides DragTool. """ if self.component: legend = self.component return (x >= legend.x and x <= legend.x2 and \ y >= legend.y and y <= legend.y2) else: return False def drag_start(self, event): """ Called when the drag operation starts. Implements DragTool. """ if self.component: self.original_padding = self.component.padding event.window.set_mouse_owner(self, event.net_transform()) event.handled = True return def dragging(self, event): """ This method is called for every mouse_move event that the tool receives while the user is dragging the mouse. Implements DragTool. Moves the legend by aligning it to a corner of its overlay component. """ # To properly move a legend (which aligns itself to a corner of its overlay # component), we need to modify the padding amounts as opposed to modifying # the position directly. if self.component: legend = self.component valign, halign = legend.align left, right, top, bottom = self.original_padding dy = int(event.y - self.mouse_down_position[1]) if valign == "u": # we subtract dy because if the mouse moves downwards, dy is # negative but the top padding has increased legend.padding_top = top - dy else: legend.padding_bottom = bottom + dy dx = int(event.x - self.mouse_down_position[0]) if halign == "r": legend.padding_right = right - dx else: legend.padding_left = left + dx event.handled = True legend.request_redraw() return def drag_end(self, event): """ Called when a mouse event causes the drag operation to end. Implements DragTool. """ # Make sure we have both a legend and that the legend is overlaying # a component if self.auto_align and self.component and self.component.component: # Determine which boundaries of the legend's overlaid component are # closest to the center of the legend legend = self.component component = legend.component left = int(legend.x - component.x) right = int(component.x2 - legend.x2) if left < right: halign = "l" legend.padding_left = left else: halign = "r" legend.padding_right = right bottom = int(legend.y - component.y) top = int(component.y2 - legend.y2) if bottom < top: valign = "l" legend.padding_bottom = bottom else: valign = "u" legend.padding_top = top legend.align = valign + halign if event.window.mouse_owner == self: event.window.set_mouse_owner(None) event.handled = True legend.request_redraw() return # EOF chaco-4.5.0/chaco/tools/line_inspector.py0000644000076600000240000002422012426452312021162 0ustar jrocherstaff00000000000000""" Defines the LineInspector tool class. """ from __future__ import with_statement # Enthought library imports from enable.api import BaseTool, ColorTrait, LineStyle from traits.api import Any, Bool, Enum, Float, Str, Trait # Chaco imports from chaco.api import BaseXYPlot, Base2DPlot class LineInspector(BaseTool): """ A simple tool to draw a line parallel to the index or the value axis of an X-Y plot. This tool supports only plots with a 1-D index. """ # The axis that this tool is parallel to. axis = Enum("index", "value", "index_x", "index_y") # The possible inspection modes of the tool. # # space: # The tool maps from screen space into the data space of the plot. # indexed: # The tool maps from screen space to an index into the plot's index array. inspect_mode = Enum("space", "indexed") # Respond to user mouse events? is_interactive = Bool(True) # Does the tool respond to updates in the metadata on the data source # and update its own position? is_listener = Bool(False) # If interactive, does the line inspector write the current data space point # to the appropriate data source's metadata? write_metadata = Bool(False) # The name of the metadata field to listen or write to. metadata_name = Str("selections") #------------------------------------------------------------------------ # Override default values of inherited traits in BaseTool #------------------------------------------------------------------------ # This tool is visible (overrides BaseTool). visible = True # This tool is drawn as an overlay (overrides BaseTool). draw_mode = "overlay" # TODO:STYLE # Color of the line. color = ColorTrait("black") # Width in pixels of the line. line_width = Float(1.0) # Dash style of the line. line_style = LineStyle("solid") # Last recorded position of the mouse _last_position = Trait(None, Any) def draw(self, gc, view_bounds=None): """ Draws this tool on a graphics context. Overrides BaseTool. """ # We draw at different points depending on whether or not we are # interactive. If both listener and interactive are true, then the # selection metadata on the plot component takes precendence. plot = self.component if plot is None: return if self.is_listener: tmp = self._get_screen_pts() elif self.is_interactive: tmp = self._last_position if tmp: sx, sy = tmp else: return if self.axis == "index" or self.axis == "index_x": if plot.orientation == "h" and sx is not None: self._draw_vertical_line(gc, sx) elif sy is not None: self._draw_horizontal_line(gc, sy) else: # self.axis == "value" if plot.orientation == "h" and sy is not None: self._draw_horizontal_line(gc, sy) elif sx is not None: self._draw_vertical_line(gc, sx) return def do_layout(self, *args, **kw): pass def overlay(self, component, gc, view_bounds=None, mode="normal"): """ Draws this component overlaid on a graphics context. """ self.draw(gc, view_bounds) return def normal_mouse_move(self, event): """ Handles the mouse being moved. """ if not self.is_interactive: return plot = self.component if plot is not None: self._last_position = (event.x, event.y) if isinstance(plot, BaseXYPlot): if self.write_metadata: if self.inspect_mode == "space": index_coord, value_coord = \ self._map_to_data(event.x, event.y) plot.index.metadata[self.metadata_name] = index_coord plot.value.metadata[self.metadata_name] = value_coord else: ndx = plot.map_index((event.x, event.y), threshold=5.0, index_only=True) if ndx: plot.index.metadata[self.metadata_name] = ndx plot.value.metadata[self.metadata_name] = ndx elif isinstance(plot, Base2DPlot): if self.write_metadata: try: old_x_data, old_y_data = \ plot.index.metadata[self.metadata_name] except: old_x_data, old_y_data = (None, None) if self.inspect_mode == "space": if plot.orientation == "h": x_coord, y_coord = \ plot.map_data([(event.x, event.y)])[0] else: y_coord, x_coord = \ plot.map_data([(event.x, event.y)])[0] if self.axis == "index_x": metadata = x_coord, old_y_data elif self.axis == "index_y": metadata = old_x_data, y_coord else: if plot.orientation == "h": x_ndx, y_ndx = plot.map_index((event.x, event.y), threshold=5.0) else: y_ndx, x_ndx = plot.map_index((event.x, event.y), threshold=5.0) if self.axis == "index_x": metadata = x_ndx, old_y_data elif self.axis == "index_y": metadata = old_x_data, y_ndx plot.index.metadata[self.metadata_name] = metadata plot.request_redraw() return def normal_mouse_leave(self, event): """ Handles the mouse leaving the plot. """ if not self.is_interactive: return self._last_position = None plot = self.component if plot is not None: if self.write_metadata: if isinstance(plot, BaseXYPlot): plot.index.metadata.pop(self.metadata_name, None) plot.value.metadata.pop(self.metadata_name, None) elif isinstance(plot, Base2DPlot): plot.index.metadata.pop(self.metadata_name, None) plot.request_redraw() return #------------------------------------------------------------------------ # Private methods #------------------------------------------------------------------------ def _get_screen_pts(self): """ Returns the screen-space coordinates of the selected point on the plot component as a tuple (x, y). A dimension that doesn't have a selected point has the value None at its index in the tuple, or won't have the key. """ plot = self.component if plot is None: return retval = [None, None] if isinstance(plot, BaseXYPlot): index_coord = plot.index.metadata.get(self.metadata_name, None) value_coord = plot.value.metadata.get(self.metadata_name, None) if index_coord not in (None, []): if self.inspect_mode == "indexed": index_coord = plot.index.get_data()[index_coord] retval[0] = plot.index_mapper.map_screen(index_coord) if value_coord not in (None, []): if self.inspect_mode == "indexed": value_coord = plot.index.get_data()[value_coord] retval[1] = plot.value_mapper.map_screen(value_coord) elif isinstance(plot, Base2DPlot): try: x_coord, y_coord = plot.index.metadata[self.metadata_name] except: x_coord, y_coord = (None, None) if x_coord not in (None, []): if self.inspect_mode == "indexed": x_coord = plot.index.get_data()[0].get_data()[x_coord] retval[0] = plot.index_mapper._xmapper.map_screen(x_coord) if y_coord not in (None, []): if self.inspect_mode == "indexed": y_coord = plot.index.get_data()[1].get_data()[y_coord] retval[1] = plot.index_mapper._ymapper.map_screen(y_coord) if plot.orientation == "h": return retval else: return retval[1], retval[0] def _map_to_data(self, x, y): """ Returns the data space coordinates of the given x and y. Takes into account orientation of the plot and the axis setting. """ plot = self.component if plot.orientation == "h": index = plot.index_mapper.map_data(x) value = plot.value_mapper.map_data(y) else: index = plot.index_mapper.map_data(y) value = plot.value_mapper.map_data(x) return index, value def _draw_vertical_line(self, gc, sx): """ Draws a vertical line through screen point (sx,sy) having the height of the tool's component. """ if sx < self.component.x or sx > self.component.x2: return with gc: gc.set_stroke_color(self.color_) gc.set_line_width(self.line_width) gc.set_line_dash(self.line_style_) gc.move_to(sx, self.component.y) gc.line_to(sx, self.component.y2) gc.stroke_path() return def _draw_horizontal_line(self, gc, sy): """ Draws a horizontal line through screen point (sx,sy) having the width of the tool's component. """ if sy < self.component.y or sy > self.component.y2: return with gc: gc.set_stroke_color(self.color_) gc.set_line_width(self.line_width) gc.set_line_dash(self.line_style_) gc.move_to(self.component.x, sy) gc.line_to(self.component.x2, sy) gc.stroke_path() return # EOF chaco-4.5.0/chaco/tools/line_segment_tool.py0000644000076600000240000003175012426452312021661 0ustar jrocherstaff00000000000000""" Defines the LineSegmentTool class. """ from __future__ import with_statement # Major library imports from numpy import array # Enthought library imports from enable.api import Component, Pointer, Line from traits.api import Any, Bool, Enum, Instance, Int, List, Trait, Tuple # Chaco imports from chaco.api import AbstractOverlay class LineSegmentTool(AbstractOverlay): """ The base class for tools that allow the user to draw a series of points connected by lines. """ # The component that this tool overlays component = Instance(Component) # The current line segment being drawn. line = Instance(Line, args=()) # A list of the points in data space as (index,value) points = List # The event states are: # # normal: # The user may have selected points, and is moving the cursor around. # selecting: # The user has clicked down but hasn't let go of the button yet, # and can still drag the point around. # dragging: # The user has clicked on an existing point and is dragging it # around. When the user releases the mouse button, the tool returns # to the "normal" state event_state = Enum("normal", "selecting", "dragging") # The pixel distance from a vertex that is considered 'on' the vertex. proximity_distance = Int(4) # The data (index, value) position of the mouse cursor; this is used by various # draw() routines. mouse_position = Trait(None, None, Tuple) # The index of the vertex being dragged, if any. _dragged = Trait(None, None, Int) # Is the point being dragged is a newly placed point? This informs the # "dragging" state about what to do if the user presses Escape while # dragging. _drag_new_point = Bool(False) # The previous event state that the tool was in. This is used for states # that can be canceled (e.g., by pressing the Escape key), so that the # tool can revert to the correct state. _prev_event_state = Any # The cursor shapes to use for various modes # Cursor shape for non-tool use. original_cursor = Pointer("arrow") # Cursor shape for drawing. normal_cursor = Pointer("pencil") # Cursor shape for deleting points. delete_cursor = Pointer("bullseye") # Cursor shape for moving points. move_cursor = Pointer("sizing") #------------------------------------------------------------------------ # Traits inherited from Component #------------------------------------------------------------------------ # The tool is initially invisible, because there is nothing to draw. visible = Bool(False) #------------------------------------------------------------------------ # Public methods #------------------------------------------------------------------------ def __init__(self, component=None, **kwtraits): if "component" in kwtraits: component = kwtraits["component"] super(LineSegmentTool, self).__init__(**kwtraits) self.component = component self.reset() self.line.line_dash = (4.0, 2.0) return #------------------------------------------------------------------------ # Drawing tool methods #------------------------------------------------------------------------ def reset(self): """ Resets the tool, throwing away any points, and making the tool invisible. """ self.points = [] self.event_state = "normal" self.visible = False self.request_redraw() return def _activate(self): """ Called by a PlotComponent when this becomes the active tool. """ pass def _deactivate(self, component=None): """ Called by a PlotComponent when this is no longer the active tool. """ self.reset() #self.component.window.set_pointer("arrow") return #------------------------------------------------------------------------ # PointLine methods #------------------------------------------------------------------------ def add_point(self, point): """ Given a screen-space *point* (x,y), adds the corresponding data space point to the list for this tool. """ self.points.append(self._map_data(point)) return def get_point(self, index): """ Retrieves the indexed point and returns its screen space value. """ return self._map_screen(self.points[index]) def set_point(self, index, point): """ Sets the data-space *index* for a screen-space *point*. """ self.points[index] = self._map_data(point) return def remove_point(self, index): """ Removes the point for a given *index* from this tool's list of points. """ del self.points[index] return #------------------------------------------------------------------------ # "normal" state #------------------------------------------------------------------------ def normal_left_down(self, event): """ Handles the left mouse button being pressed while the tool is in the 'normal' state. For an existing point, if the user is pressing the Control key, the point is deleted. Otherwise, the user can drag the point. For a new point, the point is added, and the user can drag it. """ # Determine if the user is dragging/deleting an existing point, or # creating a new one over = self._over_point(event, self.line.points) if over is not None: if event.control_down: # Delete the point self.points.pop(over) self.line.points = list(self.component.map_screen(array(self.points))) self.request_redraw() else: self.event_state = "dragging" self._dragged = over self._drag_new_point = False self.dragging_mouse_move(event) else: self.points.append(self._map_data((event.x, event.y))) self._dragged = -1 self._drag_new_point = True self.visible = True self.event_state = "dragging" self.dragging_mouse_move(event) return def normal_mouse_move(self, event): """ Handles the user moving the mouse in the 'normal' state. When the user moves the cursor over an existing point, if the Control key is pressed, the cursor changes to the **delete_cursor**, indicating that the point can be deleted. Otherwise, the cursor changes to the **move_cursor**, indicating that the point can be moved. When the user moves the cursor over any other point, the cursor changes to (or stays) the **normal_cursor**. """ # If the user moves over an existing point, change the cursor to be the # move_cursor; otherwise, set it to the normal cursor over = self._over_point(event, self.line.points) if over is not None: if event.control_down: event.window.set_pointer(self.delete_cursor) else: event.window.set_pointer(self.move_cursor) else: event.handled = False event.window.set_pointer(self.normal_cursor) self.request_redraw() return def normal_draw(self, gc): """ Draws the line. """ self.line.points = list(self.component.map_screen(array(self.points))) self.line._draw(gc) return def normal_key_pressed(self, event): """ Handles the user pressing a key in the 'normal' state. If the user presses the Enter key, the tool is reset. """ if event.character == "Enter": self._finalize_selection() self.reset() return def normal_mouse_leave(self, event): """ Handles the user moving the cursor away from the tool area. """ event.window.set_pointer("arrow") return #------------------------------------------------------------------------ # "dragging" state #------------------------------------------------------------------------ def dragging_mouse_move(self, event): """ Handles the user moving the mouse while in the 'dragging' state. The screen is updated to show the new mouse position as the end of the line segment being drawn. """ mouse_position = self._map_data((event.x, event.y)) self.points[self._dragged] = mouse_position self.line.points = list(self.component.map_screen(array(self.points))) self.request_redraw() return def dragging_draw(self, gc): """ Draws the polygon in the 'dragging' state. """ self.line._draw(gc) return def dragging_left_up(self, event): """ Handles the left mouse coming up in the 'dragging' state. Switches to 'normal' state. """ self.event_state = "normal" self._dragged = None self.updated = self return def dragging_key_pressed(self, event): """ Handles a key being pressed in the 'dragging' state. If the key is "Esc", the drag operation is canceled. """ if event.character == "Esc": self._cancel_drag() return def dragging_mouse_leave(self, event): """ Handles the mouse leaving the tool area in the 'dragging' state. The drag is canceled and the cursor changes to an arrow. """ self._cancel_drag() event.window.set_pointer("arrow") return def _cancel_drag(self): """ Cancels a drag operation. """ if self._dragged != None: if self._drag_new_point: # Only remove the point if it was a newly-placed point self.points.pop(self._dragged) self._dragged = None self.mouse_position = None self.event_state = "normal" self.request_redraw() return #------------------------------------------------------------------------ # override AbstractOverlay methods #------------------------------------------------------------------------ def overlay(self, component, gc, view_bounds, mode="normal"): """ Draws this component overlaid on another component. Implements AbstractOverlay. """ draw_func = getattr(self, self.event_state + "_draw", None) if draw_func: with gc: gc.clip_to_rect(component.x, component.y, component.width-1, component.height-1) draw_func(gc) return def request_redraw(self): """ Requests that the component redraw itself. Overrides Enable Component. """ self.component.invalidate_draw() self.component.request_redraw() return #------------------------------------------------------------------------ # Private methods #------------------------------------------------------------------------ def _map_data(self, point): """ Maps values from screen space into data space. """ x_mapper = self.component.x_mapper y_mapper = self.component.y_mapper if self.component.orientation == 'h': ndx = x_mapper.map_data(point[0]) val = y_mapper.map_data(point[1]) else: val = x_mapper.map_data(point[0]) ndx = y_mapper.map_data(point[1]) return (ndx, val) def _map_screen(self, point): """ Maps values from data space into screen space. """ x_mapper = self.component.x_mapper y_mapper = self.component.y_mapper if self.component.orientation == 'h': x = x_mapper.map_screen(point[0]) y = y_mapper.map_screen(point[1]) else: x = x_mapper.map_screen(point[1]) y = y_mapper.map_screen(point[0]) return (x, y) def _is_near_point(self, point, event): """ Determines if the pointer is near a specified point. """ event_point = (event.x, event.y) return ((abs( point[0] - event_point[0] ) + \ abs( point[1] - event_point[1] )) <= self.proximity_distance) def _over_point(self, event, points): """ Return the index of a point in *points* that *event* is 'over'. Returns None if there is no such point. """ for i, point in enumerate(points): if self._is_near_point(point, event): result = i break else: result = None return result def _finalize_selection(self): """ Abstract method called to take action after the line selection is complete """ pass #------------------------------------------------------------------------ # Trait event handlers #------------------------------------------------------------------------ def _component_changed(self, old, new): if new: self.container = new return # EOF chaco-4.5.0/chaco/tools/move_tool.py0000644000076600000240000000233112426452312020147 0ustar jrocherstaff00000000000000""" Defines the MoveTool class. """ # Enthought library imports from traits.api import Tuple from enable.tools.drag_tool import DragTool class MoveTool(DragTool): """ A tool for moving a plot component. """ # The (x,y) offset of the start of the drag relative to the component. _offset = Tuple((0,0)) def drag_start(self, event): """ Called when the drag operation starts. Implements DragTool. """ self._offset = (event.x - self.component.x, event.y - self.component.y) event.handled = True def dragging(self, event): """ This method is called for every mouse_move event that the tool receives while the user is dragging the mouse. Implements DragTool. Moves the component. """ c = self.component c.position = [event.x - self._offset[0], event.y - self._offset[1]] if getattr(c, "x_mapper", None): c.x_mapper.updated = True if getattr(c, "y_mapper", None): c.y_mapper.updated = True if getattr(c, "vgrid", None): c.vgrid.invalidate() if getattr(c, "hgrid", None): c.hgrid.invalidate() event.handled = True c.request_redraw() chaco-4.5.0/chaco/tools/pan_tool.py0000644000076600000240000002353612426462410017771 0ustar jrocherstaff00000000000000""" Defines the PanTool class. """ from numpy import inf # Enthought library imports from enable.api import BaseTool, Pointer, KeySpec from traits.api import Bool, Enum, Float, Tuple, Instance class PanTool(BaseTool): """ A tool that enables the user to pan a plot by clicking a mouse button and dragging. """ # The mouse button that initiates the drag operation. drag_button = Enum("left", "middle", "right") # The cursor to use when panning. drag_pointer = Pointer("hand") # Scaling factor on the panning "speed". speed = Float(1.0) # The modifier key that, if depressed when the drag is initiated, constrains # the panning to happen in the only direction of largest initial motion. # It is possible to permanently restrict this tool to always drag along one # direction. To do so, set constrain=True, constrain_key=None, and # constrain_direction to the desired direction. constrain_key = Enum(None, "shift", "control", "alt") # Keys to Pan via keyboard pan_right_key = Instance(KeySpec, args=("Right",)) pan_left_key = Instance(KeySpec, args=("Left",)) pan_up_key = Instance(KeySpec, args=("Up",)) pan_down_key = Instance(KeySpec, args=("Down",)) # number of pixels the keys should pan # disabled if 0.0 pan_keys_step = Float(0.0) # Constrain the panning to one direction? constrain = Bool(False) # The direction of constrained draw. A value of None means that the user # has initiated the drag and pressed the constrain_key, but hasn't moved # the mouse yet; the magnitude of the components of the next mouse_move # event will determine the constrain_direction. constrain_direction = Enum(None, "x", "y") # Restrict to the bounds of the plot data restrict_to_data = Bool(False) # (x,y) of the point where the mouse button was pressed. _original_xy = Tuple # Data coordinates of **_original_xy**. This may be either (index,value) # or (value,index) depending on the component's orientation. _original_data = Tuple # Was constrain=True triggered by the **contrain_key**? If False, it was # set programmatically. _auto_constrain = Bool(False) #------------------------------------------------------------------------ # Inherited BaseTool traits #------------------------------------------------------------------------ # The tool does not have a visual representation (overrides # BaseTool). draw_mode = "none" # The tool is not visible (overrides BaseTool). visible = False # The possible event states of this tool (overrides enable.Interactor). event_state = Enum("normal", "panning") def normal_key_pressed(self, event): """ Handles a key being pressed when the tool is in the 'normal' state. """ if self.pan_keys_step == 0.0: return src = self.component.bounds[0]/2, self.component.bounds[1]/2 dest = src if self.pan_left_key.match(event): dest = (src[0] - self.pan_keys_step, src[1]) elif self.pan_right_key.match(event): dest = (src[0] + self.pan_keys_step, src[1]) elif self.pan_down_key.match(event): dest = (src[0], src[1] - self.pan_keys_step) elif self.pan_up_key.match(event): dest = (src[0], src[1] + self.pan_keys_step) if src != dest: self._original_xy = src event.x = dest[0] event.y = dest[1] self.panning_mouse_move(event) return def normal_left_down(self, event): """ Handles the left mouse button being pressed when the tool is in the 'normal' state. Starts panning if the left mouse button is the drag button. """ if self.drag_button == "left": self._start_pan(event) return def normal_right_down(self, event): """ Handles the right mouse button being pressed when the tool is in the 'normal' state. Starts panning if the right mouse button is the drag button. """ if self.drag_button == "right": self._start_pan(event) return def normal_middle_down(self, event): """ Handles the middle mouse button being pressed when the tool is in the 'normal' state. Starts panning if the middle mouse button is the drag button. """ if self.drag_button == "middle": self._start_pan(event) return def panning_left_up(self, event): """ Handles the left mouse button coming up when the tool is in the 'panning' state. Stops panning if the left mouse button is the drag button. """ if self.drag_button == "left": self._end_pan(event) return def panning_right_up(self, event): """ Handles the right mouse button coming up when the tool is in the 'panning' state. Stops panning if the right mouse button is the drag button. """ if self.drag_button == "right": self._end_pan(event) return def panning_middle_up(self, event): """ Handles the middle mouse button coming up when the tool is in the 'panning' state. Stops panning if the middle mouse button is the drag button. """ if self.drag_button == "middle": self._end_pan(event) return def panning_mouse_move(self, event): """ Handles the mouse being moved when the tool is in the 'panning' state. """ plot = self.component if self._auto_constrain and self.constrain_direction is None: # Determine the constraint direction x_orig, y_orig = self._original_xy if abs(event.x - x_orig) > abs(event.y - y_orig): self.constrain_direction = "x" else: self.constrain_direction = "y" direction_info = [("x", "width", 0), ("y", "height", 1)] for direction, bound_name, index in direction_info: if not self.constrain or self.constrain_direction == direction: mapper = getattr(plot, direction + "_mapper") domain_min, domain_max = mapper.domain_limits eventpos = getattr(event, direction) origpos = self._original_xy[index] screenlow, screenhigh = mapper.screen_bounds screendelta = self.speed * (eventpos - origpos) newlow = mapper.map_data(screenlow - screendelta) newhigh = mapper.map_data(screenhigh - screendelta) # Don't set the range in this dimension if the panning # would exceed the domain limits. # To do this offset properly, we would need to iteratively # solve for a root using map_data on successive trial # values. As a first approximation, we're just going to # use a linear approximation, which works perfectly for # linear mappers (which is used 99% of the time). data = [arr for arr in (source.get_data() for source in mapper.range.sources) if arr.size > 0] if domain_min is None: if self.restrict_to_data: domain_min = min([arr.min() for arr in data]) else: domain_min = -inf if domain_max is None: if self.restrict_to_data: domain_max = max([arr.max() for arr in data]) else: domain_max = inf if (newlow <= domain_min) and (newhigh >= domain_max): # Don't do anything; effectively, freeze the pan continue if newlow <= domain_min: newlow = domain_min # Calculate delta in screen space, which is always linear. screen_delta = mapper.map_screen(domain_min) - screenlow newhigh = mapper.map_data(screenhigh + screen_delta) elif newhigh >= domain_max: newhigh = domain_max # Calculate delta in screen space, which is always linear. screen_delta = mapper.map_screen(domain_max) - screenhigh newlow = mapper.map_data(screenlow + screen_delta) # Use .set_bounds() so that we don't generate two range_changed # events on the DataRange mapper.range.set_bounds(newlow, newhigh) event.handled = True self._original_xy = (event.x, event.y) plot.request_redraw() return def panning_mouse_leave(self, event): """ Handles the mouse leaving the plot when the tool is in the 'panning' state. Ends panning. """ return self._end_pan(event) def _start_pan(self, event, capture_mouse=True): self._original_xy = (event.x, event.y) if self.constrain_key is not None: if getattr(event, self.constrain_key + "_down"): self.constrain = True self._auto_constrain = True self.constrain_direction = None self.event_state = "panning" if capture_mouse: event.window.set_pointer(self.drag_pointer) event.window.set_mouse_owner(self, event.net_transform()) event.handled = True return def _end_pan(self, event): if self._auto_constrain: self.constrain = False self.constrain_direction = None self.event_state = "normal" event.window.set_pointer("arrow") if event.window.mouse_owner == self: event.window.set_mouse_owner(None) event.handled = True return # EOF chaco-4.5.0/chaco/tools/pan_tool2.py0000644000076600000240000001531612426452312020050 0ustar jrocherstaff00000000000000 from numpy import inf from enable.api import Pointer from enable.tools.drag_tool import DragTool from traits.api import Bool, Enum, Float, Tuple class PanTool(DragTool): """ An implementation of a pan tool based on the DragTool instead of a bare BaseTool """ # The cursor to use when panning. drag_pointer = Pointer("hand") # Scaling factor on the panning "speed". speed = Float(1.0) # The modifier key that, if depressed when the drag is initiated, constrains # the panning to happen in the only direction of largest initial motion. # It is possible to permanently restrict this tool to always drag along one # direction. To do so, set constrain=True, constrain_key=None, and # constrain_direction to the desired direction. constrain_key = Enum(None, "shift", "control", "alt") # Constrain the panning to one direction? constrain = Bool(False) # The direction of constrained draw. A value of None means that the user # has initiated the drag and pressed the constrain_key, but hasn't moved # the mouse yet; the magnitude of the components of the next mouse_move # event will determine the constrain_direction. constrain_direction = Enum(None, "x", "y") # Restrict to the bounds of the plot data restrict_to_data = Bool(False) # (x,y) of the point where the mouse button was pressed. _original_xy = Tuple # Data coordinates of **_original_xy**. This may be either (index,value) # or (value,index) depending on the component's orientation. _original_data = Tuple # Was constrain=True triggered by the **contrain_key**? If False, it was # set programmatically. _auto_constrain = Bool(False) #------------------------------------------------------------------------ # Inherited BaseTool traits #------------------------------------------------------------------------ # The tool does not have a visual representation (overrides # BaseTool). draw_mode = "none" # The tool is not visible (overrides BaseTool). visible = False # The possible event states of this tool (overrides enable.Interactor). #event_state = Enum("normal", "panning") def drag_start(self, event): """ Called when the drag operation starts """ self._start_pan(event) def dragging(self, event): plot = self.component if self._auto_constrain and self.constrain_direction is None: # Determine the constraint direction if abs(event.x - self._original_xy[0]) > abs(event.y - self._original_xy[1]): self.constrain_direction = "x" else: self.constrain_direction = "y" for direction, bound_name, ndx in [("x","width",0), ("y","height",1)]: if not self.constrain or self.constrain_direction == direction: mapper = getattr(plot, direction + "_mapper") range = mapper.range domain_min, domain_max = mapper.domain_limits eventpos = getattr(event, direction) origpos = self._original_xy[ndx] screenlow, screenhigh = mapper.screen_bounds screendelta = self.speed * (eventpos - origpos) #if getattr(plot, direction + "_direction", None) == "flipped": # screendelta = -screendelta newlow = mapper.map_data(screenlow - screendelta) newhigh = mapper.map_data(screenhigh - screendelta) # Don't set the range in this dimension if the panning # would exceed the domain limits. # To do this offset properly, we would need to iteratively # solve for a root using map_data on successive trial # values. As a first approximation, we're just going to # use a linear approximation, which works perfectly for # linear mappers (which is used 99% of the time). if domain_min is None: if self.restrict_to_data: domain_min = min([source.get_data().min() for source in range.sources]) else: domain_min = -inf if domain_max is None: if self.restrict_to_data: domain_max = max([source.get_data().max() for source in range.sources]) else: domain_max = inf if (newlow <= domain_min) and (newhigh >= domain_max): # Don't do anything; effectively, freeze the pan continue if newlow <= domain_min: delta = newhigh - newlow newlow = domain_min # Don't let the adjusted newhigh exceed domain_max; this # can happen with a nonlinear mapper. newhigh = min(domain_max, domain_min + delta) elif newhigh >= domain_max: delta = newhigh - newlow newhigh = domain_max # Don't let the adjusted newlow go below domain_min; this # can happen with a nonlinear mapper. newlow = max(domain_min, domain_max - delta) # Use .set_bounds() so that we don't generate two range_changed # events on the DataRange range.set_bounds(newlow, newhigh) event.handled = True self._original_xy = (event.x, event.y) plot.request_redraw() return def drag_cancel(self, event): # We don't do anything for "cancelling" of the drag event because its # transient states during the drag are generally valid and useful # terminal states unto themselves. pass def drag_end(self, event): return self._end_pan(event) def _start_pan(self, event, capture_mouse=True): self._original_xy = (event.x, event.y) if self.constrain_key is not None: if getattr(event, self.constrain_key + "_down"): self.constrain = True self._auto_constrain = True self.constrain_direction = None self.event_state = "panning" if capture_mouse: event.window.set_pointer(self.drag_pointer) event.window.set_mouse_owner(self, event.net_transform()) event.handled = True return def _end_pan(self, event): if self._auto_constrain: self.constrain = False self.constrain_direction = None self.event_state = "normal" event.window.set_pointer("arrow") if event.window.mouse_owner == self: event.window.set_mouse_owner(None) event.handled = True return chaco-4.5.0/chaco/tools/point_marker.py0000755000076600000240000000534712426452312020653 0ustar jrocherstaff00000000000000""" Defines the PointMarker tool class. """ from __future__ import with_statement # Major library imports from numpy import array, take, transpose # Enthought library imports from enable.api import BaseTool, ColorTrait from traits.api import Enum, Float class PointMarker(BaseTool): """ This tool looks at an XY plot's index data source and draws a line corresponding to the index indicated by the "selections" metadata. """ # The axis that this tool is parallel to. axis = Enum("index", "value") # This tool is visible (overrides BaseTool). visible = True # This tool is drawn as an overlay (overrides BaseTool). draw_mode = "overlay" # TODO:STYLE # The color of the line. color = ColorTrait("red") # The width of the line, in pixels. line_width = Float(1.0) def draw(self, gc, view_bounds=None): """ Draws this tool on a graphics context. Implements BaseTool. """ # Draw the component in interactive mode plot = self.component if plot is not None: # selections should be a list of indices on the datasource indices = getattr(plot, self.axis).metadata["selections"] if len(indices) == 0: return index_pts = take(plot.index.get_data(), indices) value_pts = take(plot.value.get_data(), indices) data_pts = transpose(array((index_pts, value_pts))) screen_pts = plot.map_screen(data_pts) if self.axis == "index": if plot.orientation == "h": self._draw_vertical_lines(gc, screen_pts) else: self._draw_horizontal_lines(gc, screen_pts) else: # self.axis == "value" if plot.orientation == "h": self._draw_horizontal_lines(gc, screen_pts) else: self._draw_vertical_lines(gc, screen_pts) return #------------------------------------------------------------------------ # Private methods #------------------------------------------------------------------------ def _draw_vertical_lines(self, gc, points): with gc: gc.set_stroke_color(self.color_) for pt in points: gc.move_to(int(pt[0])+0.5, self.component.y) gc.line_to(int(pt[0])+0.5, self.component.y2) gc.stroke_path() return def _draw_horizontal_lines(self, gc, points): with gc: gc.set_stroke_color(self.color_) for pt in points: gc.move_to(self.component.x, int(pt[1])+0.5) gc.line_to(self.component.x2, int(pt[1])+0.5) gc.stroke_path() return # EOF chaco-4.5.0/chaco/tools/range_selection.py0000644000076600000240000006251512426462425021325 0ustar jrocherstaff00000000000000""" Defines the RangeSelection controller class. """ # Major library imports from numpy import array # Enthought library imports from traits.api import Any, Array, Bool, Enum, Event, Float, Int, Instance, \ List, Property, Str, Trait, Tuple from enable.api import KeySpec # Chaco imports from chaco.api import AbstractController class RangeSelection(AbstractController): """ Selects a range along the index or value axis. The user right-click-drags to select a region, which stays selected until the user left-clicks to deselect. """ # The axis to which this tool is perpendicular. axis = Enum("index", "value") # The selected region, expressed as a tuple in data space. This updates # and fires change-events as the user is dragging. selection = Property selection_mode = Enum("set", "append") # This event is fired whenever the user completes the selection, or when a # finalized selection gets modified. The value of the event is the data # space range. selection_completed = Event # The name of the metadata on the datasource that we will write # self.selection to metadata_name = Str("selections") # Either "set" or "append", depending on whether self.append_key was # held down selection_mode_metadata_name = Str("selection_mode") # The name of the metadata on the datasource that we will set to a numpy # boolean array for masking the datasource's data mask_metadata_name = Str("selection_masks") # The possible event states of this selection tool (overrides # enable.Interactor). # # normal: # Nothing has been selected, and the user is not dragging the mouse. # selecting: # The user is dragging the mouse and actively changing the # selection region; resizing of an existing selection also # uses this mode. # selected: # The user has released the mouse and a selection has been # finalized. The selection remains until the user left-clicks # or self.deselect() is called. # moving: # The user moving (not resizing) the selection range. event_state = Enum("normal", "selecting", "selected", "moving") #------------------------------------------------------------------------ # Traits for overriding default object relationships # # By default, the RangeSelection assumes that self.component is a plot # and looks for the mapper and the axis_index on it. If this is not the # case, then any (or all) three of these can be overriden by directly # assigning values to them. To unset them and have them revert to default # behavior, assign "None" to them. #------------------------------------------------------------------------ # The plot associated with this tool By default, this is just # self.component. plot = Property # The mapper for associated with this tool. By default, this is the mapper # on **plot** that corresponds to **axis**. mapper = Property # The index to use for **axis**. By default, this is self.plot.orientation, # but it can be overriden and set to 0 or 1. axis_index = Property # List of listeners that listen to selection events. listeners = List #------------------------------------------------------------------------ # Configuring interaction control #------------------------------------------------------------------------ # Can the user resize the selection once it has been drawn? enable_resize = Bool(True) # The pixel distance between the mouse event and a selection endpoint at # which the user action will be construed as a resize operation. resize_margin = Int(7) # Allow the left button begin a selection? left_button_selects = Bool(False) # Disable all left-mouse button interactions? disable_left_mouse = Bool(False) # Allow the tool to be put into the deselected state via mouse clicks allow_deselection = Bool(True) # The minimum span, in pixels, of a selection region. Any attempt to # select a region smaller than this will be treated as a deselection. minimum_selection = Int(5) # The key which, if held down while the mouse is being dragged, will # indicate that the selection should be appended to an existing selection # as opposed to overwriting it. append_key = Instance(KeySpec, args=(None, "control")) #------------------------------------------------------------------------ # Private traits #------------------------------------------------------------------------ # The value of the override plot to use, if any. If None, then uses # self.component. _plot = Trait(None, Any) # The value of the override mapper to use, if any. If None, then uses the # mapper on self.component. _mapper = Trait(None, Any) # Shadow trait for the **axis_index** property. _axis_index = Trait(None, None, Int) # The data space start and end coordinates of the selected region, # expressed as a list. _selection = Trait(None, None, Tuple, List, Array) # The selection in mask form. _selection_mask = Array # The end of the selection that is being actively modified by the mouse. _drag_edge = Enum("high", "low") #------------------------------------------------------------------------ # These record the mouse position when the user is moving (not resizing) # the selection #------------------------------------------------------------------------ # The position of the initial user click for moving the selection. _down_point = Array # (x,y) # The data space coordinates of **_down_point**. _down_data_coord = Float # The original selection when the mouse went down to move the selection. _original_selection = Any #------------------------------------------------------------------------ # Public methods #------------------------------------------------------------------------ def deselect(self, event=None): """ Deselects the highlighted region. This method essentially resets the tool. It takes the event causing the deselection as an optional argument. """ self.selection = None self.selection_completed = None self.event_state = "normal" self.component.request_redraw() if event: event.window.set_pointer("arrow") event.handled = True return #------------------------------------------------------------------------ # Event handlers for the "selected" event state #------------------------------------------------------------------------ def selected_left_down(self, event): """ Handles the left mouse button being pressed when the tool is in the 'selected' state. If the user is allowed to resize the selection, and the event occurred within the resize margin of an endpoint, then the tool switches to the 'selecting' state so that the user can resize the selection. If the event is within the bounds of the selection region, then the tool switches to the 'moving' states. Otherwise, the selection becomes deselected. """ if self.disable_left_mouse: return screen_bounds = self._get_selection_screencoords() if screen_bounds is None: self.deselect(event) return low = min(screen_bounds) high = max(screen_bounds) tmp = (event.x, event.y) ndx = self.axis_index mouse_coord = tmp[ndx] if self.enable_resize: if (abs(mouse_coord - high) <= self.resize_margin) or \ (abs(mouse_coord - low) <= self.resize_margin): return self.selected_right_down(event) if low <= tmp[ndx] <= high: self.event_state = "moving" self._down_point = array([event.x, event.y]) self._down_data_coord = \ self.mapper.map_data(self._down_point)[ndx] self._original_selection = array(self.selection) elif self.allow_deselection: self.deselect(event) else: # Treat this as a combination deselect + left down self.deselect(event) self.normal_left_down(event) event.handled = True return def selected_right_down(self, event): """ Handles the right mouse button being pressed when the tool is in the 'selected' state. If the user is allowed to resize the selection, and the event occurred within the resize margin of an endpoint, then the tool switches to the 'selecting' state so that the user can resize the selection. Otherwise, the selection becomes deselected, and a new selection is started.. """ if self.enable_resize: coords = self._get_selection_screencoords() if coords is not None: start, end = coords tmp = (event.x, event.y) ndx = self.axis_index mouse_coord = tmp[ndx] # We have to do a little swapping; the "end" point # is always what gets updated, so if the user # clicked on the starting point, we have to reverse # the sense of the selection. if abs(mouse_coord - end) <= self.resize_margin: self.event_state = "selecting" self._drag_edge = "high" self.selecting_mouse_move(event) elif abs(mouse_coord - start) <= self.resize_margin: self.event_state = "selecting" self._drag_edge = "low" self.selecting_mouse_move(event) #elif self.allow_deselection: # self.deselect(event) else: # Treat this as a combination deselect + right down self.deselect(event) self.normal_right_down(event) else: # Treat this as a combination deselect + right down self.deselect(event) self.normal_right_down(event) event.handled = True return def selected_mouse_move(self, event): """ Handles the mouse moving when the tool is in the 'selected' srate. If the user is allowed to resize the selection, and the event occurred within the resize margin of an endpoint, then the cursor changes to indicate that the selection could be resized. Otherwise, the cursor is set to an arrow. """ if self.enable_resize: # Change the mouse cursor when the user moves within the # resize margin coords = self._get_selection_screencoords() if coords is not None: start, end = coords tmp = (event.x, event.y) ndx = self.axis_index mouse_coord = tmp[ndx] if abs(mouse_coord - end) <= self.resize_margin or \ abs(mouse_coord - start) <= self.resize_margin: self._set_sizing_cursor(event) return event.window.set_pointer("arrow") event.handled = True return def selected_mouse_leave(self, event): """ Handles the mouse leaving the plot when the tool is in the 'selected' state. Sets the cursor to an arrow. """ event.window.set_pointer("arrow") return #------------------------------------------------------------------------ # Event handlers for the "moving" event state #------------------------------------------------------------------------ def moving_left_up(self, event): """ Handles the left mouse button coming up when the tool is in the 'moving' state. Switches the tool to the 'selected' state. """ if self.disable_left_mouse: return self.event_state = "selected" self.selection_completed = self.selection self._down_point = [] event.handled = True return def moving_mouse_move(self, event): """ Handles the mouse moving when the tool is in the 'moving' state. Moves the selection range by an amount corresponding to the amount that the mouse has moved since its button was pressed. If the new selection range overlaps the endpoints of the data, it is truncated to that endpoint. """ cur_point = array([event.x, event.y]) cur_data_point = self.mapper.map_data(cur_point)[self.axis_index] original_selection = self._original_selection new_selection = original_selection + (cur_data_point - self._down_data_coord) selection_data_width = original_selection[1] - original_selection[0] range = self.mapper.range if min(new_selection) < range.low: new_selection = (range.low, range.low + selection_data_width) elif max(new_selection) > range.high: new_selection = (range.high - selection_data_width, range.high) self.selection = new_selection self.selection_completed = new_selection self.component.request_redraw() event.handled = True return def moving_mouse_leave(self, event): """ Handles the mouse leaving the plot while the tool is in the 'moving' state. If the mouse was within the selection region when it left, the method does nothing. If the mouse was outside the selection region whe it left, the event is treated as moving the selection to the minimum or maximum. """ axis_index = self.axis_index low = self.plot.position[axis_index] high = low + self.plot.bounds[axis_index] - 1 pos = self._get_axis_coord(event) if pos >= low and pos <= high: # the mouse left but was within the mapping range, so don't do # anything return else: # the mouse left and exceeds the mapping range, so we need to slam # the selection all the way to the minimum or the maximum self.moving_mouse_move(event) return def moving_mouse_enter(self, event): if not event.left_down: return self.moving_left_up(event) return #------------------------------------------------------------------------ # Event handlers for the "normal" event state #------------------------------------------------------------------------ def normal_left_down(self, event): """ Handles the left mouse button being pressed when the tool is in the 'normal' state. If the tool allows the left mouse button to start a selection, then it does so. """ if self.left_button_selects: return self.normal_right_down(event) def normal_right_down(self, event): """ Handles the right mouse button being pressed when the tool is in the 'normal' state. Puts the tool into 'selecting' mode, changes the cursor to show that it is selecting, and starts defining the selection. """ pos = self._get_axis_coord(event) mapped_pos = self.mapper.map_data(pos) self.selection = (mapped_pos, mapped_pos) self._set_sizing_cursor(event) self._down_point = array([event.x, event.y]) self.event_state = "selecting" if self.append_key is not None and self.append_key.match(event): self.selection_mode = "append" else: self.selection_mode = "set" self.selecting_mouse_move(event) return #------------------------------------------------------------------------ # Event handlers for the "selecting" event state #------------------------------------------------------------------------ def selecting_mouse_move(self, event): """ Handles the mouse being moved when the tool is in the 'selecting' state. Expands the selection range at the appropriate end, based on the new mouse position. """ if self.selection is not None: axis_index = self.axis_index low = self.plot.position[axis_index] high = low + self.plot.bounds[axis_index] - 1 tmp = self._get_axis_coord(event) if tmp >= low and tmp <= high: new_edge = self.mapper.map_data(self._get_axis_coord(event)) #new_edge = self._map_data(self._get_axis_coord(event)) if self._drag_edge == "high": low_val = self.selection[0] if new_edge >= low_val: self.selection = (low_val, new_edge) else: self.selection = (new_edge, low_val) self._drag_edge = "low" else: high_val = self.selection[1] if new_edge <= high_val: self.selection = (new_edge, high_val) else: self.selection = (high_val, new_edge) self._drag_edge = "high" self.component.request_redraw() event.handled = True return def selecting_button_up(self, event): # Check to see if the selection region is bigger than the minimum event.window.set_pointer("arrow") end = self._get_axis_coord(event) if len(self._down_point) == 0: cancel_selection = False else: start = self._down_point[self.axis_index] self._down_point = [] cancel_selection = self.minimum_selection > abs(start - end) if cancel_selection: self.deselect(event) event.handled = True else: self.event_state = "selected" # Fire the "completed" event self.selection_completed = self.selection event.handled = True return def selecting_right_up(self, event): """ Handles the right mouse button coming up when the tool is in the 'selecting' state. Switches the tool to the 'selected' state and completes the selection. """ self.selecting_button_up(event) def selecting_left_up(self, event): """ Handles the left mouse button coming up when the tool is in the 'selecting' state. Switches the tool to the 'selected' state. """ if self.disable_left_mouse: return self.selecting_button_up(event) def selecting_mouse_leave(self, event): """ Handles the mouse leaving the plot when the tool is in the 'selecting' state. Determines whether the event's position is outside the component's bounds, and if so, clips the selection. Sets the cursor to an arrow. """ axis_index = self.axis_index low = self.plot.position[axis_index] high = low + self.plot.bounds[axis_index] - 1 old_selection = self.selection selection_low = old_selection[0] selection_high = old_selection[1] pos = self._get_axis_coord(event) if pos >= high: # clip to the boundary appropriate for the mapper's orientation. if self.mapper.sign == 1: selection_high = self.mapper.map_data(high) else: selection_high = self.mapper.map_data(low) elif pos <= low: if self.mapper.sign == 1: selection_low = self.mapper.map_data(low) else: selection_low = self.mapper.map_data(high) self.selection = (selection_low, selection_high) event.window.set_pointer("arrow") self.component.request_redraw() return def selecting_mouse_enter(self, event): """ Handles the mouse entering the plot when the tool is in the 'selecting' state. If the mouse does not have the right mouse button down, this event is treated as if the right mouse button was released. Otherwise, the method sets the cursor to show that it is selecting. """ # If we were in the "selecting" state when the mouse left, and # the mouse has entered without a button being down, # then treat this like we got a button up event. if not (event.right_down or event.left_down): return self.selecting_button_up(event) else: self._set_sizing_cursor(event) return #------------------------------------------------------------------------ # Property getter/setters #------------------------------------------------------------------------ def _get_plot(self): if self._plot is not None: return self._plot else: return self.component def _set_plot(self, val): self._plot = val return def _get_mapper(self): if self._mapper is not None: return self._mapper else: return getattr(self.plot, self.axis + "_mapper") def _set_mapper(self, new_mapper): self._mapper = new_mapper return def _get_axis_index(self): if self._axis_index is None: return self._determine_axis() else: return self._axis_index def _set_axis_index(self, val): self._axis_index = val return def _get_selection(self): selection = getattr(self.plot, self.axis).metadata[self.metadata_name] return selection def _set_selection(self, val): oldval = self._selection self._selection = val datasource = getattr(self.plot, self.axis, None) if datasource is not None: mdname = self.metadata_name # Set the selection range on the datasource datasource.metadata[mdname] = val datasource.metadata_changed = {mdname: val} # Set the selection mask on the datasource selection_masks = \ datasource.metadata.setdefault(self.mask_metadata_name, []) for index in range(len(selection_masks)): if id(selection_masks[index]) == id(self._selection_mask): del selection_masks[index] break # Set the selection mode on the datasource datasource.metadata[self.selection_mode_metadata_name] = \ self.selection_mode if val is not None: low, high = val data_pts = datasource.get_data() new_mask = (data_pts >= low) & (data_pts <= high) selection_masks.append(new_mask) self._selection_mask = new_mask datasource.metadata_changed = {self.mask_metadata_name: val} self.trait_property_changed("selection", oldval, val) for l in self.listeners: if hasattr(l, "set_value_selection"): l.set_value_selection(val) return #------------------------------------------------------------------------ # Private methods #------------------------------------------------------------------------ def _get_selection_screencoords(self): """ Returns a tuple of (x1, x2) screen space coordinates of the start and end selection points. If there is no current selection, then it returns None. """ selection = self.selection if selection is not None and len(selection) == 2: return self.mapper.map_screen(array(selection)) else: return None def _set_sizing_cursor(self, event): """ Sets the correct cursor shape on the window of the event, given the tool's orientation and axis. """ if self.axis_index == 0: # horizontal range selection, so use left/right arrow event.window.set_pointer("size left") else: # vertical range selection, so use up/down arrow event.window.set_pointer("size top") return def _get_axis_coord(self, event, axis="index"): """ Returns the coordinate of the event along the axis of interest to this tool (or along the orthogonal axis, if axis="value"). """ event_pos = (event.x, event.y) if axis == "index": return event_pos[self.axis_index] else: return event_pos[1 - self.axis_index] def _determine_axis(self): """ Determines whether the index of the coordinate along this tool's axis of interest is the first or second element of an (x,y) coordinate tuple. This method is only called if self._axis_index hasn't been set (or is None). """ if self.axis == "index": if self.plot.orientation == "h": return 0 else: return 1 else: # self.axis == "value" if self.plot.orientation == "h": return 1 else: return 0 def __mapper_changed(self): self.deselect() return def _axis_changed(self, old, new): if old is not None: self.plot.on_trait_change(self.__mapper_changed, old + "_mapper", remove=True) if new is not None: self.plot.on_trait_change(self.__mapper_changed, old + "_mapper", remove=True) return # EOF chaco-4.5.0/chaco/tools/range_selection_2d.py0000644000076600000240000003003012426452312021667 0ustar jrocherstaff00000000000000""" Defines the RangeSelection controller class. """ # Major library imports import numpy # Chaco imports from range_selection import RangeSelection class RangeSelection2D(RangeSelection): """ Selects a range along the index or value axis for plots on 2D data, such as image plots The user right-click-drags to select a region, which stays selected until the user left-clicks to deselect. """ #------------------------------------------------------------------------ # Event handlers for the "selected" event state #------------------------------------------------------------------------ def selected_left_down(self, event): """ Handles the left mouse button being pressed when the tool is in the 'selected' state. If the user is allowed to resize the selection, and the event occurred within the resize margin of an endpoint, then the tool switches to the 'selecting' state so that the user can resize the selection. If the event is within the bounds of the selection region, then the tool switches to the 'moving' states. Otherwise, the selection becomes deselected. """ screen_bounds = self._get_selection_screencoords() if screen_bounds is None: self.deselect(event) return low = min(screen_bounds) high = max(screen_bounds) tmp = (event.x, event.y) ndx = self._determine_axis() mouse_coord = tmp[ndx] if self.enable_resize: if (abs(mouse_coord - high) <= self.resize_margin) or \ (abs(mouse_coord - low) <= self.resize_margin): return self.selected_right_down(event) if tmp[self.axis_index] >= low and tmp[self.axis_index] <= high: self.event_state = "moving" self._down_point = numpy.array([event.x, event.y]) self._down_data_coord = self._map_data([self._down_point])[0][self.axis_index] self._original_selection = numpy.array(self.selection) elif self.allow_deselection: self.deselect(event) else: # Treat this as a combination deselect + left down self.deselect(event) self.normal_left_down(event) event.handled = True return def selected_right_down(self, event): """ Handles the right mouse button being pressed when the tool is in the 'selected' state. If the user is allowed to resize the selection, and the event occurred within the resize margin of an endpoint, then the tool switches to the 'selecting' state so that the user can resize the selection. Otherwise, the selection becomes deselected, and a new selection is started.. """ if self.enable_resize: coords = self._get_selection_screencoords() if coords is not None: start, end = coords tmp = (event.x, event.y) ndx = self._determine_axis() mouse_coord = tmp[ndx] # We have to do a little swapping; the "end" point # is always what gets updated, so if the user # clicked on the starting point, we have to reverse # the sense of the selection. if abs(mouse_coord - end) <= self.resize_margin: self.event_state = "selecting" self._drag_edge = "high" self.selecting_mouse_move(event) elif abs(mouse_coord - start) <= self.resize_margin: self.event_state = "selecting" self._drag_edge = "low" self.selecting_mouse_move(event) elif self.allow_deselection: self.deselect(event) else: # Treat this as a combination deselect + right down self.deselect(event) self.normal_right_down(event) else: # Treat this as a combination deselect + right down self.deselect(event) self.normal_right_down(event) event.handled = True return def selected_mouse_move(self, event): """ Handles the mouse moving when the tool is in the 'selected' state. If the user is allowed to resize the selection, and the event occurred within the resize margin of an endpoint, then the cursor changes to indicate that the selection could be resized. Otherwise, the cursor is set to an arrow. """ if self.enable_resize: # Change the mouse cursor when the user moves within the resize margin coords = self._get_selection_screencoords() if coords is not None: start, end = coords tmp = (event.x, event.y) ndx = self._determine_axis() mouse_coord = tmp[ndx] if abs(mouse_coord - end) <= self.resize_margin or \ abs(mouse_coord - start) <= self.resize_margin: self._set_sizing_cursor(event) return event.window.set_pointer("arrow") event.handled = True return #------------------------------------------------------------------------ # Event handlers for the "moving" event state #------------------------------------------------------------------------ def moving_mouse_move(self, event): """ Handles the mouse moving when the tool is in the 'moving' state. Moves the selection range by an amount corresponding to the amount that the mouse has moved since its button was pressed. If the new selection range overlaps the endpoints of the data, it is truncated to that endpoint. """ cur_point = numpy.array([event.x, event.y]) cur_data_point = self._map_data([cur_point])[0] original_selection = self._original_selection new_selection = original_selection + (cur_data_point[self.axis_index] \ - self._down_data_coord) selection_data_width = original_selection[1] - original_selection[0] range = self.mapper.range range_low = range.low[self.axis_index] range_high = range.high[self.axis_index] if min(new_selection) < range_low: new_selection = (range_low, range_low + selection_data_width) elif max(new_selection) > range_high: new_selection = (range_high - selection_data_width, range_high) self.selection = new_selection self.selection_completed = new_selection self.component.request_redraw() event.handled = True return #------------------------------------------------------------------------ # Event handlers for the "normal" event state #------------------------------------------------------------------------ def normal_right_down(self, event): """ Handles the right mouse button being pressed when the tool is in the 'normal' state. Puts the tool into 'selecting' mode, changes the cursor to show that it is selecting, and starts defining the selection. """ x_pos = self._get_axis_coord(event, "index") y_pos = self._get_axis_coord(event, "value") self._down_point = numpy.array([x_pos, y_pos]) mapped_pos = self._map_data([(x_pos,y_pos)])[0][self.axis_index] self.selection = (mapped_pos, mapped_pos) self._set_sizing_cursor(event) self.event_state = "selecting" self.selecting_mouse_move(event) return #------------------------------------------------------------------------ # Event handlers for the "selecting" event state #------------------------------------------------------------------------ def selecting_mouse_move(self, event): """ Handles the mouse being moved when the tool is in the 'selecting' state. Expands the selection range at the appropriate end, based on the new mouse position. """ if self.selection is not None: axis_index = self.axis_index low = self.plot.position[axis_index] high = low + self.plot.bounds[axis_index] - 1 tmp = self._get_axis_coord(event) if tmp >= low and tmp <= high: x_pos = self._get_axis_coord(event, "index") y_pos = self._get_axis_coord(event, "value") new_edge = self._map_data([(x_pos,y_pos)])[0][self.axis_index] if self._drag_edge == "high": low_val = self.selection[0] # the selection should be a range consisting of 2 points, # if it appears that only 1 point is selected, move one # edge over a pixel if new_edge == low_val: new_edge = self._map_data([(x_pos+1,y_pos+1)])[0][self.axis_index] if new_edge > low_val: self.selection = (low_val, new_edge) else: self.selection = (new_edge, low_val) self._drag_edge = "low" else: high_val = self.selection[1] # the selection should be a range consisting of 2 points, # if it appears that only 1 point is selected, move one # edge over a pixel if new_edge == high_val: new_edge = self._map_data([(x_pos-1,y_pos-1)])[0][self.axis_index] if new_edge < high_val: self.selection = (new_edge, high_val) else: self.selection = (high_val, new_edge) self._drag_edge = "high" self.component.request_redraw() event.handled = True return def selecting_mouse_leave(self, event): """ Handles the mouse leaving the plot when the tool is in the 'selecting' state. Determines whether the event's position is outside the component's bounds, and if so, clips the selection. Sets the cursor to an arrow. """ axis_index = self.axis_index low = self.plot.position[axis_index] high = low + self.plot.bounds[axis_index] - 1 old_selection = self.selection selection_low = old_selection[0] selection_high = old_selection[1] pos = self._get_axis_coord(event) if pos >= high: if self.axis == 'index': selection_high = self._map_data([(high, 0)])[0][self.axis_index] else: selection_high = self._map_data([(0, high)])[0][self.axis_index] elif pos <= low: if self.axis == 'index': selection_low = self._map_data([(low, 0)])[0][self.axis_index] else: selection_low = self._map_data([(0, low)])[0][self.axis_index] self.selection = (selection_low, selection_high) event.window.set_pointer("arrow") self.component.request_redraw() return #------------------------------------------------------------------------ # Private methods #------------------------------------------------------------------------ def _map_data(self, screen_pts): return self.mapper.map_data(screen_pts) def _map_screen(self, data_pts): return self.mapper.map_screen(data_pts) def _get_selection_screencoords(self): """ Returns a tuple of (x1, x2) screen space coordinates of the start and end selection points. If there is no current selection, then it returns None. """ selection = self.selection if selection is not None and len(selection) == 2: if self.axis == 'index': return [x for x,y in self._map_screen([(x,0) for x in self.selection])] else: return [y for x,y in self._map_screen([(0,y) for y in self.selection])] else: return None chaco-4.5.0/chaco/tools/range_selection_overlay.py0000644000076600000240000001633012426452312023052 0ustar jrocherstaff00000000000000""" Defines the RangeSelectionOverlay class. """ from __future__ import with_statement # Major library imports from numpy import arange, array # Enthought library imports from enable.api import ColorTrait, LineStyle from traits.api import Enum, Float, Property, Str, Instance, \ cached_property from chaco.api import AbstractOverlay, arg_find_runs, GridMapper, AbstractMapper class RangeSelectionOverlay(AbstractOverlay): """ Highlights the selection region on a component. Looks at a given metadata field of self.component for regions to draw as selected. """ # The axis to which this tool is perpendicular. axis = Enum("index", "value") # Mapping from screen space to data space. By default, it is just # self.component. plot = Property(depends_on='component') # The mapper (and associated range) that drive this RangeSelectionOverlay. # By default, this is the mapper on self.plot that corresponds to self.axis. mapper = Instance(AbstractMapper) # The element of an (x,y) tuple that corresponds to the axis index. # By default, this is set based on self.asix and self.plot.orientation, # but it can be overriden and set to 0 or 1. axis_index = Property # The name of the metadata to look at for dataspace bounds. The metadata # can be either a tuple (dataspace_start, dataspace_end) in "selections" or # a boolean array mask of seleted dataspace points with any other name metadata_name = Str("selections") #------------------------------------------------------------------------ # Appearance traits #------------------------------------------------------------------------ # The color of the selection border line. border_color = ColorTrait("dodgerblue") # The width, in pixels, of the selection border line. border_width = Float(1.0) # The line style of the selection border line. border_style = LineStyle("solid") # The color to fill the selection region. fill_color = ColorTrait("lightskyblue") # The transparency of the fill color. alpha = Float(0.3) #------------------------------------------------------------------------ # AbstractOverlay interface #------------------------------------------------------------------------ def overlay(self, component, gc, view_bounds=None, mode="normal"): """ Draws this component overlaid on another component. Overrides AbstractOverlay. """ axis_ndx = self.axis_index lower_left = [0,0] upper_right = [0,0] # Draw the selection coords = self._get_selection_screencoords() for coord in coords: start, end = coord lower_left[axis_ndx] = start lower_left[1-axis_ndx] = component.position[1-axis_ndx] upper_right[axis_ndx] = end - start upper_right[1-axis_ndx] = component.bounds[1-axis_ndx] with gc: gc.clip_to_rect(component.x, component.y, component.width, component.height) gc.set_alpha(self.alpha) gc.set_fill_color(self.fill_color_) gc.set_stroke_color(self.border_color_) gc.set_line_width(self.border_width) gc.set_line_dash(self.border_style_) gc.draw_rect((lower_left[0], lower_left[1], upper_right[0], upper_right[1])) #------------------------------------------------------------------------ # Private methods #------------------------------------------------------------------------ def _get_selection_screencoords(self): """ Returns a tuple of (x1, x2) screen space coordinates of the start and end selection points. If there is no current selection, then returns an empty list. """ ds = getattr(self.plot, self.axis) selection = ds.metadata.get(self.metadata_name, None) if selection is None: return [] # "selections" metadata must be a tuple if self.metadata_name == "selections" or \ (selection is not None and isinstance(selection, tuple)): if selection is not None and len(selection) == 2: return [self.mapper.map_screen(array(selection))] else: return [] # All other metadata is interpreted as a mask on dataspace else: ar = arange(0,len(selection), 1) runs = arg_find_runs(ar[selection]) coords = [] for inds in runs: start = ds._data[ar[selection][inds[0]]] end = ds._data[ar[selection][inds[1]-1]] coords.append(self.mapper.map_screen(array((start, end)))) return coords def _determine_axis(self): """ Determines which element of an (x,y) coordinate tuple corresponds to the tool's axis of interest. This method is only called if self._axis_index hasn't been set (or is None). """ if self.axis == "index": if self.plot.orientation == "h": return 0 else: return 1 else: # self.axis == "value" if self.plot.orientation == "h": return 1 else: return 0 #------------------------------------------------------------------------ # Trait event handlers #------------------------------------------------------------------------ def _component_changed(self, old, new): self._attach_metadata_handler(old, new) return def _axis_changed(self, old, new): self._attach_metadata_handler(old, new) return def _attach_metadata_handler(self, old, new): # This is used to attach a listener to the datasource so that when # its metadata has been updated, we catch the event and update properly if not self.plot: return datasource = getattr(self.plot, self.axis) if old: datasource.on_trait_change(self._metadata_change_handler, "metadata_changed", remove=True) if new: datasource.on_trait_change(self._metadata_change_handler, "metadata_changed") return def _metadata_change_handler(self, event): self.component.request_redraw() return #------------------------------------------------------------------------ # Default initializers #------------------------------------------------------------------------ def _mapper_default(self): # If the plot's mapper is a GridMapper, return either its # x mapper or y mapper mapper = getattr(self.plot, self.axis + "_mapper") if isinstance(mapper, GridMapper): if self.axis == 'index': return mapper._xmapper else: return mapper._ymapper else: return mapper #------------------------------------------------------------------------ # Property getter/setters #------------------------------------------------------------------------ @cached_property def _get_plot(self): return self.component @cached_property def _get_axis_index(self): return self._determine_axis() # EOF chaco-4.5.0/chaco/tools/rect_zoom.py0000644000076600000240000000073012426452312020146 0ustar jrocherstaff00000000000000""" Defines the RectZoomTool class. """ from zoom_tool import ZoomTool class RectZoomTool(ZoomTool): """ Allows the user to drag a zoom box around a region of the plot. This is a subclass of ZoomTool, with different default values for some traits. """ # Selects a box in two dimensions (overrides SimpleZoom). tool_mode = "box" # The tool is always on; left-clicking initiates a zoom (overrides # SimpleZoom). always_on = True chaco-4.5.0/chaco/tools/regression_lasso.py0000644000076600000240000000640612426452312021534 0ustar jrocherstaff00000000000000""" Defines the RegressionLasso class. """ from __future__ import with_statement # Major library imports from numpy import compress, polyfit from math import fabs # Enthought library imports from enable.api import ColorTrait, LineStyle from traits.api import Any, Float, Instance # Chaco imports from chaco.api import LassoOverlay, Label from chaco.tools.api import LassoSelection class RegressionLasso(LassoSelection): """ A controller for "lassoing" a selection of points in a regression plot. """ # The regression updates as more points are added (overrides LassoSelection). incremental_select = True # Tuple (slope, intercept) of the line that fits the data. fit_params = Any # The center point of the selected points, in data space. centroid = Any def _selection_changed_fired(self, event): indices = self.selection_datasource.metadata["selection"] if any(indices): x = compress(indices, self.component.index.get_data()) y = compress(indices, self.component.value.get_data()) if len(x) < 2 or len(y) < 2: self.fit_params = None self.centroid = None else: self.fit_params = tuple(polyfit(x,y,1)) self.centroid = (sum(x)/len(x)), (sum(y)/len(y)) else: self.fit_params = None self.centroid = None return class RegressionOverlay(LassoOverlay): line_color = ColorTrait("black") line_style = LineStyle("dash") line_width = Float(2.0) _label = Instance(Label, kw=dict(bgcolor="white", border_color="black", font="modern 14", border_width=1)) def _draw_component(self, gc, view_bounds=None, mode="normal"): LassoOverlay._draw_component(self, gc, view_bounds, mode) selection = self.lasso_selection if selection.fit_params is not None: # draw the label overlay self._label.component = self.component c = self.component if selection.fit_params[1] < 0: operator = "-" else: operator = "+" self._label.text = "%.2fx "%selection.fit_params[0] + operator + \ " %.2f" % fabs(selection.fit_params[1]) w, h = self._label.get_width_height(gc) x = (c.x+c.x2)/2 - w/2 y = c.y + 5 # add some padding on the bottom with gc: gc.translate_ctm(x, y) self._label.draw(gc) # draw the line slope, y0 = selection.fit_params f = lambda x: slope*x + y0 cx, cy = c.map_screen([selection.centroid])[0] left = c.x right = c.x2 left_x = c.map_data([left, c.y])[0] right_x = c.map_data([right, c.y])[0] left_y = f(left_x) right_y = f(right_x) left_pt, right_pt = c.map_screen([[left_x, left_y], [right_x, right_y]]) with gc: gc.set_line_dash(self.line_style_) gc.set_stroke_color(self.line_color_) gc.set_line_width(self.line_width) gc.move_to(*left_pt) gc.line_to(*right_pt) gc.stroke_path() return chaco-4.5.0/chaco/tools/save_tool.py0000755000076600000240000000457012426452312020151 0ustar jrocherstaff00000000000000""" Defines the SaveTool class. """ # Major library imports import os.path # Enthought library imports from traits.api import Enum, Str, Tuple from enable.api import BaseTool class SaveTool(BaseTool): """ This tool allows the user to press Ctrl+S to save a snapshot image of the plot component. """ # The file that the image is saved in. The format will be deduced from # the extension. filename = Str("saved_plot.png") #------------------------------------------------------------------------- # PDF format options # This mirror the traits in PdfPlotGraphicsContext. #------------------------------------------------------------------------- pagesize = Enum("letter", "A4") dest_box = Tuple((0.5, 0.5, -0.5, -0.5)) dest_box_units = Enum("inch", "cm", "mm", "pica") #------------------------------------------------------------------------- # Override default trait values inherited from BaseTool #------------------------------------------------------------------------- # This tool does not have a visual representation (overrides BaseTool). draw_mode = "none" # This tool is not visible (overrides BaseTool). visible = False def normal_key_pressed(self, event): """ Handles a key-press when the tool is in the 'normal' state. Saves an image of the plot if the keys pressed are Control and S. """ if self.component is None: return if event.character == "s" and event.control_down: if os.path.splitext(self.filename)[-1] == ".pdf": self._save_pdf() else: self._save_raster() event.handled = True return def _save_raster(self): """ Saves an image of the component. """ from chaco.api import PlotGraphicsContext gc = PlotGraphicsContext((int(self.component.outer_width), int(self.component.outer_height))) self.component.draw(gc, mode="normal") gc.save(self.filename) return def _save_pdf(self): from chaco.pdf_graphics_context import PdfPlotGraphicsContext gc = PdfPlotGraphicsContext(filename=self.filename, pagesize = self.pagesize, dest_box = self.dest_box, dest_box_units = self.dest_box_units) gc.render_component(self.component) gc.save() # EOF chaco-4.5.0/chaco/tools/scatter_inspector.py0000644000076600000240000001201012426452312021672 0ustar jrocherstaff00000000000000""" Defines the ScatterInspector tool class. """ # Enthought library imports from traits.api import Bool, Str # Local, relative imports from select_tool import SelectTool class ScatterInspector(SelectTool): """ A tool for inspecting scatter plots. It writes the index of the point under the cursor to the metadata of the index and value data sources, and allows clicking to select the point. Other components can listen for metadata updates on the data sources. By default, it writes the index of the point under the cursor to the "hover" key in metadata, and the index of a clicked point to "selection". """ # If persistent_hover is False, then a point will be de-hovered as soon as # the mouse leaves its hittesting area. If persistent_hover is True, then # a point does no de-hover until another point get hover focus. persistent_hover = Bool(False) # The names of the data source metadata for hover and selection. hover_metadata_name = Str('hover') selection_metadata_name = Str('selections') #------------------------------------------------------------------------ # Override/configure inherited traits #------------------------------------------------------------------------ # This tool is not visible visible = False # This tool does not have a visual reprentation draw_mode = "none" def normal_mouse_move(self, event): """ Handles the mouse moving when the tool is in the 'normal' state. If the cursor is within **threshold** of a data point, the method writes the index to the plot's data sources' "hover" metadata. """ plot = self.component index = plot.map_index((event.x, event.y), threshold=self.threshold) if index is not None: plot.index.metadata[self.hover_metadata_name] = [index] if hasattr(plot, "value"): plot.value.metadata[self.hover_metadata_name] = [index] elif not self.persistent_hover: plot.index.metadata.pop(self.hover_metadata_name, None) if hasattr(plot, "value"): plot.value.metadata.pop(self.hover_metadata_name, None) return def _get_selection_state(self, event): plot = self.component index = plot.map_index((event.x, event.y), threshold=self.threshold) #index_md = plot.index.metadata.get(self.selection_metadata_name, None) #value_md = plot.value.metadata.get(self.selection_metadata_name, None) already_selected = False for name in ('index', 'value'): if not hasattr(plot, name): continue md = getattr(plot, name).metadata if md is None or self.selection_metadata_name not in md: continue if index in md[self.selection_metadata_name]: already_selected = True break return already_selected, (index is not None) def _get_selection_token(self, event): plot = self.component index = plot.map_index((event.x, event.y), threshold=self.threshold) return index def _deselect(self, index=None): """ Deselects a particular index. If no index is given, then deselects all points. """ plot = self.component for name in ('index', 'value'): if not hasattr(plot, name): continue md = getattr(plot, name).metadata if not self.selection_metadata_name in md: pass elif index in md[self.selection_metadata_name]: new_list = md[self.selection_metadata_name][:] new_list.remove(index) md[self.selection_metadata_name] = new_list getattr(plot, name).metadata_changed = True return def _select(self, index, append=True): plot = self.component for name in ('index', 'value'): if not hasattr(plot, name): continue md = getattr(plot, name).metadata selection = md.get(self.selection_metadata_name, None) # If no existing selection if selection is None: md[self.selection_metadata_name] = [index] # check for list-like object supporting append else: if append: if index not in md[self.selection_metadata_name]: new_list = md[self.selection_metadata_name] + [index] md[self.selection_metadata_name] = new_list # Manually trigger the metadata_changed event on # the datasource. Datasources only automatically # fire notifications when the values inside the # metadata dict change, but they do not listen # for further changes on those values. getattr(plot, name).metadata_changed = True else: md[self.selection_metadata_name] = [index] return # EOF chaco-4.5.0/chaco/tools/select_tool.py0000644000076600000240000000776712426452312020502 0ustar jrocherstaff00000000000000 # Enthought library imports from enable.api import BaseTool, KeySpec from traits.api import Enum, Float, Instance class SelectTool(BaseTool): """ Base class for tools that handle some level of click-to-select interaction. Handles the logic of different kinds of selection modes. Subclasses only need to implement a few concrete methods to handle actual selection/deselection. """ # The threshold, in pixels, around the cursor location to search for points. threshold = Float(5.0) # How selections are handled: # # "toggle" # The user clicks on points (while optionally holding down a modifier # key) to select or deselect them. If the point is already selected, # clicking it again deselects it. The modifier key to use is set by # **multiselect_modifier**. The only way to deselect points is by # clicking on them; clicking on a screen space outside of the plot does # not deselect points. # "multi" # Like **toggle** mode, except that the user can deselect all points # at once by clicking on the plot area away from a point. # "single" # The user can only select a single point at a time. # "off" # The user cannot select points via clicking. selection_mode = Enum("toggle", "multi", "single", "off") # The modifier key to use to multi-select points. Only used in **toggle** # and **multi** selection modes. multiselect_modifier = Instance(KeySpec, args=(None, "control"), allow_none=True) def _get_selection_state(self, event): """ Returns a tuple reflecting the current selection state Parameters ---------- event : enable KeyEvent or MouseEvent Returns ------- (already_selected, clicked) : tuple of Bool clicked is True if the item corresponding to the input event has just been clicked. already_selected indicates that the item corresponding to the input event is already selected. """ raise NotImplementedError def _get_selection_token(self, event): """ Returns a token corresponding to the selection event. This token is passed in to the select and deselect methods. By default, this just returns the event itself. """ return event def _select(self, token, append=True): """ Selects the given token. """ raise NotImplementedError def _deselect(self, token, append=True): """ Deselects the given token. """ raise NotImplementedError def normal_left_down(self, event): """ Handles the left mouse button being pressed when the tool is in the 'normal' state. If selecting is enabled and the cursor is within **threshold** of a data point, the method calls the subclass's _select" or _deselect methods to perform the appropriate action, given the current selection_mode. """ if self.selection_mode != "off": already_selected, clicked = self._get_selection_state(event) modifier_down = self.multiselect_modifier.match(event) token = self._get_selection_token(event) if (self.selection_mode == "single") or\ (self.selection_mode == "multi" and not modifier_down): if clicked and not already_selected: if self.selection_mode == "single" or not modifier_down: self._select(token, append=False) else: self._select(token, append=True) event.handled = True else: self._deselect(token) else: # multi or toggle, and modifier_down is true if clicked: if already_selected: self._deselect(token) else: self._select(token) event.handled = True return chaco-4.5.0/chaco/tools/simple_inspector.py0000644000076600000240000001163312426452312021530 0ustar jrocherstaff00000000000000"""Simple Inspector tool for plots This module provides a simple tool that reports the data-space coordinates of the current mouse cursor position in a plot. It is intended for use with SimpleInspectorOverlay, but other objects can potentially hook into its API. """ from chaco.image_plot import ImagePlot from enable.api import BaseTool, KeySpec from traits.api import Bool, Event, Tuple, Enum, Callable class SimpleInspectorTool(BaseTool): """ Simple inspector tool for plots This is a simple tool that reports the data-space coordinates of the current mouse cursor position in a plot. Interested overlays and other objects can listen for new_value events, which is a dictionary of data about the current location in data space, and can look at the last_mouse_position trait which holds the mouse position in screen space. The tool also provides a visible trait which listeners can use to hide themselves. By default the 'p' key toggles this. Instances can provide a value_generator function that performs computations to generate additional values in the dictionary that is passed to the new_value event. Subclasses can override gather_values() to similar effect. """ # This event fires whenever the mouse moves over a new image point. # Its value is a dict with default keys "x", "y", "index" and "value". new_value = Event # Indicates whether overlays listening to this tool should be visible. visible = Bool(True) # Stores the last mouse position. This can be used by overlays to # position themselves around the mouse. last_mouse_position = Tuple # This key will show and hide any overlays listening to this tool. inspector_key = KeySpec('p') # A callable that computes other values for the new_value event # this takes a dictionary as an argument, and returns a dictionary value_generator = Callable # Private Trails ######################################################## # Stores the value of self.visible when the mouse leaves the tool, # so that it can be restored when the mouse enters again. _old_visible = Enum(None, True, False) #Trait(None, Bool(True)) ######################################################################### # SimpleInspectorTool API ######################################################################### def gather_values(self, event): """ Generate the values for the new_value dictionary. By default this returns a dictionary with keys "x", "y", "index" and "value". If there is a value_generator callable, this will be called to modify the dictionary. Parameters ---------- event The mouse_move event. Returns ------- A dictionary. """ x, y, index, value = self.map_to_data(event.x, event.y) d = {'index': index, 'value': value, 'x': x, 'y': y} if isinstance(self.component, ImagePlot): x_ndx, y_ndx = self.component.map_index((event.x, event.y), outside_returns_none=False) # FIXME: off-by-one error. The size of the index is +1 to the size of # the image array if y_ndx == self.component.value.data.shape[0]: y_ndx -= 1 if x_ndx == self.component.value.data.shape[1]: x_ndx += 1 z = self.component.value.data[y_ndx, x_ndx] d['z'] = z d['color'] = z if self.value_generator is not None: d = self.value_generator(d) return d def map_to_data(self, x, y): """ Returns the data space coordinates of the given x and y. Takes into account orientation of the plot and the axis setting. """ plot = self.component if plot.orientation == "h": index = x = plot.x_mapper.map_data(x) value = y = plot.y_mapper.map_data(y) else: index = y = plot.y_mapper.map_data(y) value = x = plot.x_mapper.map_data(x) return x, y, index, value ######################################################################### # Component API ######################################################################### def normal_key_pressed(self, event): if self.inspector_key.match(event): self.visible = not self.visible def normal_mouse_leave(self, event): if self._old_visible is None: self._old_visible = self.visible self.visible = False def normal_mouse_enter(self, event): if self._old_visible is not None: self.visible = self._old_visible self._old_visible = None def normal_mouse_move(self, event): plot = self.component if plot is not None: self.new_value = self.gather_values(event) self.last_mouse_position = (event.x, event.y) chaco-4.5.0/chaco/tools/simple_zoom.py0000644000076600000240000006101012426452312020500 0ustar jrocherstaff00000000000000""" Defines the SimpleZoom class. """ from __future__ import with_statement import warnings warnings.warn("SimpleZoom has been deprecated, use ZoomTool", DeprecationWarning) from numpy import array # Enthought library imports from enable.api import ColorTrait, KeySpec from traits.api \ import Bool, Enum, Float, Instance, Int, Str, Trait, Tuple # Chaco imports from chaco.abstract_overlay import AbstractOverlay from base_zoom_tool import BaseZoomTool from tool_history_mixin import ToolHistoryMixin class SimpleZoom(AbstractOverlay, ToolHistoryMixin, BaseZoomTool): """ Selects a range along the index or value axis. The user left-click-drags to select a region to zoom in. Certain keyboard keys are mapped to performing zoom actions as well. Implements a basic "zoom stack" so the user move go backwards and forwards through previous zoom regions. """ # The selection mode: # # range: # Select a range across a single index or value axis. # box: # Perform a "box" selection on two axes. tool_mode = Enum("box", "range") # Is the tool always "on"? If True, left-clicking always initiates # a zoom operation; if False, the user must press a key to enter zoom mode. always_on = Bool(False) # Defines a meta-key, that works with always_on to set the zoom mode. This # is useful when the zoom tool is used in conjunction with the pan tool. always_on_modifier = Enum(None, 'shift', 'control', 'alt') #------------------------------------------------------------------------- # Zoom control #------------------------------------------------------------------------- # The axis to which the selection made by this tool is perpendicular. This # only applies in 'range' mode. axis = Enum("index", "value") #------------------------------------------------------------------------- # Interaction control #------------------------------------------------------------------------- # Enable the mousewheel for zooming? enable_wheel = Bool(True) # The mouse button that initiates the drag. If "None", then the tool # will not respond to drag. (It can still respond to mousewheel events.) drag_button = Enum("left", "right", None) # Conversion ratio from wheel steps to zoom factors. wheel_zoom_step = Float(1.0) # The key press to enter zoom mode, if **always_on** is False. Has no effect # if **always_on** is True. enter_zoom_key = Instance(KeySpec, args=("z",)) # The key press to leave zoom mode, if **always_on** is False. Has no effect # if **always_on** is True. exit_zoom_key = Instance(KeySpec, args=("z",)) # Disable the tool after the zoom is completed? disable_on_complete = Bool(True) # The minimum amount of screen space the user must select in order for # the tool to actually take effect. minimum_screen_delta = Int(10) #------------------------------------------------------------------------- # Appearance properties (for Box mode) #------------------------------------------------------------------------- # The pointer to use when drawing a zoom box. pointer = "magnifier" # The color of the selection box. color = ColorTrait("lightskyblue") # The alpha value to apply to **color** when filling in the selection # region. Because it is almost certainly useless to have an opaque zoom # rectangle, but it's also extremely useful to be able to use the normal # named colors from Enable, this attribute allows the specification of a # separate alpha value that replaces the alpha value of **color** at draw # time. alpha = Trait(0.4, None, Float) # The color of the outside selection rectangle. border_color = ColorTrait("dodgerblue") # The thickness of selection rectangle border. border_size = Int(1) # The possible event states of this zoom tool. event_state = Enum("normal", "selecting") #------------------------------------------------------------------------ # Key mappings #------------------------------------------------------------------------ # The key that cancels the zoom and resets the view to the original defaults. cancel_zoom_key = Instance(KeySpec, args=("Esc",)) #------------------------------------------------------------------------ # Private traits #------------------------------------------------------------------------ # If **always_on** is False, this attribute indicates whether the tool # is currently enabled. _enabled = Bool(False) # the original numerical screen ranges _orig_low_setting = Trait(None, Tuple, Float, Str) _orig_high_setting = Trait(None, Tuple, Float, Str) # The (x,y) screen point where the mouse went down. _screen_start = Trait(None, None, Tuple) # The (x,,y) screen point of the last seen mouse move event. _screen_end = Trait(None, None, Tuple) def __init__(self, component=None, *args, **kw): # Support AbstractController-style constructors so that this can be # handed in the component it will be overlaying in the constructor # without using kwargs. self.component = component super(SimpleZoom, self).__init__(*args, **kw) self._reset_state_to_current() if self.tool_mode == "range": mapper = self._get_mapper() self._orig_low_setting = mapper.range.low_setting self._orig_high_setting = mapper.range.high_setting else: x_range = self.component.x_mapper.range y_range = self.component.y_mapper.range self._orig_low_setting = (x_range.low_setting, y_range.low_setting) self._orig_high_setting = \ (x_range.high_setting, y_range.high_setting) component.on_trait_change(self._reset_state_to_current, "index_data_changed") return def enable(self, event=None): """ Provides a programmatic way to enable this tool, if **always_on** is False. Calling this method has the same effect as if the user pressed the **enter_zoom_key**. """ if self.component.active_tool != self: self.component.active_tool = self self._enabled = True if event and event.window: event.window.set_pointer(self.pointer) return def disable(self, event=None): """ Provides a programmatic way to enable this tool, if **always_on** is False. Calling this method has the same effect as if the user pressed the **exit_zoom_key**. """ self.reset() self._enabled = False if self.component.active_tool == self: self.component.active_tool = None if event and event.window: event.window.set_pointer("arrow") return def reset(self, event=None): """ Resets the tool to normal state, with no start or end position. """ self.event_state = "normal" self._screen_start = None self._screen_end = None def deactivate(self, component): """ Called when this is no longer the active tool. """ # Required as part of the AbstractController interface. return self.disable() def overlay(self, component, gc, view_bounds=None, mode="normal"): """ Draws this component overlaid on another component. Overrides AbstractOverlay. """ if self.event_state == "selecting": if self.tool_mode == "range": self.overlay_range(component, gc) else: self.overlay_box(component, gc) return def overlay_box(self, component, gc): """ Draws the overlay as a box. """ if self._screen_start and self._screen_end: with gc: gc.set_antialias(0) gc.set_line_width(self.border_size) gc.set_stroke_color(self.border_color_) gc.clip_to_rect(component.x, component.y, component.width, component.height) x, y = self._screen_start x2, y2 = self._screen_end rect = (x, y, x2-x+1, y2-y+1) if self.color != "transparent": if self.alpha: color = list(self.color_) if len(color) == 4: color[3] = self.alpha else: color += [self.alpha] else: color = self.color_ gc.set_fill_color(color) gc.rect(*rect) gc.draw_path() else: gc.rect(*rect) gc.stroke_path() return def overlay_range(self, component, gc): """ Draws the overlay as a range. """ axis_ndx = self._determine_axis() lower_left = [0,0] upper_right = [0,0] lower_left[axis_ndx] = self._screen_start[axis_ndx] lower_left[1-axis_ndx] = self.component.position[1-axis_ndx] upper_right[axis_ndx] = self._screen_end[axis_ndx] - self._screen_start[axis_ndx] upper_right[1-axis_ndx] = self.component.bounds[1-axis_ndx] with gc: gc.set_antialias(0) gc.set_alpha(self.alpha) gc.set_fill_color(self.color_) gc.set_stroke_color(self.border_color_) gc.clip_to_rect(component.x, component.y, component.width, component.height) gc.rect(lower_left[0], lower_left[1], upper_right[0], upper_right[1]) gc.draw_path() return def normal_left_down(self, event): """ Handles the left mouse button being pressed while the tool is in the 'normal' state. If the tool is enabled or always on, it starts selecting. """ if self._is_enabling_event(event): self._start_select(event) event.handled = True return def normal_right_down(self, event): """ Handles the right mouse button being pressed while the tool is in the 'normal' state. If the tool is enabled or always on, it starts selecting. """ if self._is_enabling_event(event): self._start_select(event) event.handled = True return def selecting_mouse_move(self, event): """ Handles the mouse moving when the tool is in the 'selecting' state. The selection is extended to the current mouse position. """ self._screen_end = (event.x, event.y) self.component.request_redraw() event.handled = True return def selecting_left_up(self, event): """ Handles the left mouse button being released when the tool is in the 'selecting' state. Finishes selecting and does the zoom. """ if self.drag_button == "left": self._end_select(event) return def selecting_right_up(self, event): """ Handles the right mouse button being released when the tool is in the 'selecting' state. Finishes selecting and does the zoom. """ if self.drag_button == "right": self._end_select(event) return def selecting_mouse_leave(self, event): """ Handles the mouse leaving the plot when the tool is in the 'selecting' state. Ends the selection operation without zooming. """ self._end_selecting(event) return def selecting_key_pressed(self, event): """ Handles a key being pressed when the tool is in the 'selecting' state. If the key pressed is the **cancel_zoom_key**, then selecting is canceled. """ if self.cancel_zoom_key.match(event): self._end_selecting(event) event.handled = True return def _start_select(self, event): """ Starts selecting the zoom region """ if self.component.active_tool in (None, self): self.component.active_tool = self else: self._enabled = False self._screen_start = (event.x, event.y) self._screen_end = None self.event_state = "selecting" event.window.set_pointer(self.pointer) event.window.set_mouse_owner(self, event.net_transform()) self.selecting_mouse_move(event) return def _end_select(self, event): """ Ends selection of the zoom region, adds the new zoom range to the zoom stack, and does the zoom. """ self._screen_end = (event.x, event.y) start = array(self._screen_start) end = array(self._screen_end) if sum(abs(end - start)) < self.minimum_screen_delta: self._end_selecting(event) event.handled = True return if self.tool_mode == "range": mapper = self._get_mapper() axis = self._determine_axis() low = mapper.map_data(self._screen_start[axis]) high = mapper.map_data(self._screen_end[axis]) if low > high: low, high = high, low else: low, high = self._map_coordinate_box(self._screen_start, self._screen_end) new_zoom_range = (low, high) self._append_state(new_zoom_range) self._do_zoom() self._end_selecting(event) event.handled = True return def _end_selecting(self, event=None): """ Ends selection of zoom region, without zooming. """ if self.disable_on_complete: self.disable(event) else: self.reset() self.component.request_redraw() if event and event.window.mouse_owner == self: event.window.set_mouse_owner(None) return def _do_zoom(self): """ Does the zoom operation. This method does not handle zooms triggered by scrolling the mouse wheel. Those are handled by `normal_mouse_wheel()`. """ # Sets the bounds on the component using _history_index. low, high = self._current_state() orig_low, orig_high = self._history[0] if self._history_index == 0: # Reset to the original range(s). if self.tool_mode == "range": # "range" mode; reset the one axis. mapper = self._get_mapper() mapper.range.low_setting = self._orig_low_setting mapper.range.high_setting = self._orig_high_setting else: # "box" mode; reset both axes. x_range = self.component.x_mapper.range y_range = self.component.y_mapper.range x_range.low_setting, y_range.low_setting = \ self._orig_low_setting x_range.high_setting, y_range.high_setting = \ self._orig_high_setting # resetting the ranges will allow 'auto' to pick the values x_range.reset() y_range.reset() else: # Do a new zoom. if self.tool_mode == "range": # "range" mode; zoom the one axis. mapper = self._get_mapper() if self._zoom_limit_reached(orig_low, orig_high, low, high, mapper): self._pop_state() return mapper.range.low = low mapper.range.high = high else: # "box" mode; zoom both axes. for ndx in (0, 1): mapper = (self.component.x_mapper, self.component.y_mapper)[ndx] if self._zoom_limit_reached(orig_low[ndx], orig_high[ndx], low[ndx], high[ndx], mapper): # pop _current_state off the stack and leave the actual # bounds unmodified. self._pop_state() return x_range = self.component.x_mapper.range y_range = self.component.y_mapper.range x_range.low, y_range.low = low x_range.high, y_range.high = high self.component.request_redraw() return def normal_key_pressed(self, event): """ Handles a key being pressed when the tool is in 'normal' state. If the tool is not always on, this method handles turning it on and off when the appropriate keys are pressed. Also handles keys to manipulate the tool history. """ if not self.always_on: if not self._enabled and self.enter_zoom_key.match(event): if self.component.active_tool in (None, self): self.component.active_tool = self self._enabled = True event.window.set_pointer(self.pointer) else: self._enabled = False return elif self._enabled and self.exit_zoom_key.match(event): self._enabled = False event.window.set_pointer("arrow") return self._history_handle_key(event) if event.handled: self.component.request_redraw() return def normal_mouse_wheel(self, event): """ Handles the mouse wheel being used when the tool is in the 'normal' state. Scrolling the wheel "up" zooms in; scrolling it "down" zooms out. """ if self.enable_wheel and event.mouse_wheel != 0: if event.mouse_wheel > 0: # zoom in zoom = 1.0 / (1.0 + 0.5 * self.wheel_zoom_step) elif event.mouse_wheel < 0: # zoom out zoom = 1.0 + 0.5 * self.wheel_zoom_step # We'll determine the current position of the cursor in screen coordinates, # and only afterwards map to dataspace. c = self.component screenlow_pt, screenhigh_pt = (c.x, c.y), (c.x2, c.y2) mouse_pos = (event.x, event.y) if self.tool_mode == "range": mapper_list = [(self._determine_axis(), self._get_mapper())] else: mapper_list = [(0, c.x_mapper), (1, c.y_mapper)] orig_low, orig_high = self._history[0] # If any of the axes reaches its zoom limit, we should cancel the zoom. # We should first calculate the new ranges and store them. If none of # the axes reach zoom limit, we can apply the new ranges. todo_list = [] for ndx, mapper in mapper_list: screenrange = mapper.screen_bounds mouse_val = mouse_pos[ndx] newscreenlow = mouse_val + zoom * (screenlow_pt[ndx] - mouse_val) newscreenhigh = mouse_val + zoom * (screenhigh_pt[ndx] - mouse_val) newlow = mapper.map_data(newscreenlow) newhigh = mapper.map_data(newscreenhigh) if type(orig_high) in (tuple,list): ol, oh = orig_low[ndx], orig_high[ndx] else: ol, oh = orig_low, orig_high if self._zoom_limit_reached(ol, oh, newlow, newhigh, mapper): # Ignore other axes, we're done. event.handled = True return todo_list.append((mapper,newlow,newhigh)) # Check the domain limits on each dimension, and rescale the zoom # amount if necessary. for ndx, (mapper, newlow, newhigh) in enumerate(todo_list): if newlow > newhigh: # This happens when the orientation of the axis is reversed. newlow, newhigh = newhigh, newlow domain_min, domain_max = getattr(mapper, "domain_limits", (None,None)) if domain_min is not None and newlow < domain_min: newlow = domain_min if domain_max is not None and newhigh > domain_max: newhigh = domain_max todo_list[ndx] = (mapper, newlow, newhigh) # All axes can be rescaled, do it. for mapper, newlow, newhigh in todo_list: if newlow > newhigh: newlow, newhigh = newhigh, newlow mapper.range.set_bounds(newlow, newhigh) event.handled = True c.request_redraw() return def _is_enabling_event(self, event): always_on = self.always_on if self.always_on_modifier == 'shift': always_on = always_on and event.shift_down elif self.always_on_modifier == 'control': always_on = always_on and event.control_down elif self.always_on_modifier == 'alt': always_on = always_on and event.alt_down if always_on or self._enabled: if event.right_down and self.drag_button == 'right': return True if event.left_down and self.drag_button == 'left': return True return False def _component_changed(self): if self.traits_inited() and self._get_mapper() is not None: self._reset_state_to_current() return def _tool_mode_changed(self, old, new): if not self.traits_inited(): return # The history must be reset because the different tool modes keep # different state types in the history self._reset_state_to_current() #------------------------------------------------------------------------ # Implementation of PlotComponent interface #------------------------------------------------------------------------ def _activate(self): """ Called by PlotComponent to set this as the active tool. """ self.enable() #------------------------------------------------------------------------ # implementations of abstract methods on ToolHistoryMixin #------------------------------------------------------------------------ def _reset_state_to_current(self): """ Clears the tool history, and sets the current state to be the first state in the history. """ if self.tool_mode == "range": mapper = self._get_mapper() if mapper is not None: self._reset_state((mapper.range.low, mapper.range.high)) else: if self.component.x_mapper is not None: x_range = self.component.x_mapper.range xlow = x_range.low xhigh = x_range.high else: xlow = "auto" xhigh = "auto" if self.component.y_mapper is not None: y_range = self.component.y_mapper.range ylow = y_range.low yhigh = y_range.high else: ylow = "auto" yhigh = "auto" self._reset_state(((xlow, ylow), (xhigh, yhigh))) def _reset_state_pressed(self): """ Called when the tool needs to reset its history. The history index will have already been set to 0. Implements ToolHistoryMixin. """ # First zoom to the set state (ZoomTool handles setting the index=0). self._do_zoom() # Now reset the state to the current bounds settings. self._reset_state_to_current() return def _prev_state_pressed(self): """ Called when the tool needs to advance to the previous state in the stack. The history index will have already been set to the index corresponding to the prev state. Implements ToolHistoryMixin. """ self._do_zoom() return def _next_state_pressed(self): """ Called when the tool needs to advance to the next state in the stack. The history index will have already been set to the index corresponding to the next state. Implements ToolHistoryMixin. """ self._do_zoom() return ### Persistence ########################################################### def __getstate__(self): dont_pickle = [ 'always_on', 'always_on_modifier', 'enter_zoom_key', 'exit_zoom_key', 'minimum_screen_delta', 'event_state', 'reset_zoom_key', 'prev_zoom_key', 'next_zoom_key', 'pointer', '_enabled', '_screen_start', '_screen_end'] state = super(SimpleZoom,self).__getstate__() for key in dont_pickle: if state.has_key(key): del state[key] return state chaco-4.5.0/chaco/tools/tests/0000755000076600000240000000000012426466422016744 5ustar jrocherstaff00000000000000chaco-4.5.0/chaco/tools/tests/__init__.py0000644000076600000240000000000012426462410021034 0ustar jrocherstaff00000000000000chaco-4.5.0/chaco/tools/tests/pan_tool_test_case.py0000644000076600000240000000232712426462410023160 0ustar jrocherstaff00000000000000import unittest import numpy as np from chaco.array_plot_data import ArrayPlotData from chaco.plot import Plot from chaco.tools.pan_tool import PanTool from enable.testing import EnableTestAssistant class PanToolTestCase(EnableTestAssistant, unittest.TestCase): def test_restrict_to_data_with_empty_source(self): # Regression test for #214. plot_data = ArrayPlotData() plot = Plot(plot_data) arr = np.arange(4.0) plot_data.set_data("x", arr) plot_data.set_data("y", arr) plot_data.set_data("z", np.array([], np.float64)) plot.plot(('x', 'y')) plot.plot(('z', 'z')) tool = PanTool(plot, restrict_to_data=True) plot.tools.append(tool) x_range = plot.x_mapper.range y_range = plot.y_mapper.range x_bounds = (x_range.low, x_range.high) y_bounds = (y_range.low, y_range.high) self.mouse_down(tool, 0.0, 0.0) self.mouse_move(interactor=tool, x=1.0, y=1.0) self.mouse_up(interactor=tool, x=1.0, y=1.0) self.assertEqual((x_range.low, x_range.high), x_bounds) self.assertEqual((y_range.low, y_range.high), y_bounds) if __name__ == '__main__': import nose nose.run() chaco-4.5.0/chaco/tools/tests/range_selection_test_case.py0000644000076600000240000000364312426462410024510 0ustar jrocherstaff00000000000000import unittest import numpy as np from chaco.array_plot_data import ArrayPlotData from chaco.plot import Plot from chaco.tools.range_selection import RangeSelection from enable.testing import EnableTestAssistant class RangeSelectionTestCase(EnableTestAssistant, unittest.TestCase): def test_selecting_mouse_leave_clipping(self): # Regression test for #216. plot_data = ArrayPlotData() arr = np.arange(4.0) plot_data.set_data("x", arr) plot_data.set_data("y", arr) for origin in ('bottom left', 'top left', 'bottom right', 'top right'): for orientation in ('h', 'v'): for axis in ('index', 'value'): plot = Plot( plot_data, orientation=orientation, origin='top right' ) renderer = plot.plot(('x', 'y'))[0] renderer.bounds = [10, 20] tool = RangeSelection( renderer, left_button_selects=True, axis=axis, ) renderer.tools.append(tool) low_x, low_y = plot.position high_x = low_x + renderer.bounds[0] - 1 high_y = low_y + renderer.bounds[1] - 1 cx = 5 cy = 5 bounds = ( (low_x - 1, low_y), (high_x + 1, low_y), (low_x, low_y - 1), (low_x, high_y + 1), ) for x, y in bounds: self.mouse_down(tool, x=cx, y=cy) self.mouse_leave(tool, x=x, y=y) selection = tool.selection self.assertTrue(selection[0] <= selection[1]) self.mouse_up(tool, x=x, y=y) if __name__ == '__main__': import nose nose.run() chaco-4.5.0/chaco/tools/tool_history_mixin.py0000644000076600000240000001132212426452312022106 0ustar jrocherstaff00000000000000""" Defines the ToolHistoryMixin class. """ from traits.api import HasTraits, Instance, Int, List from enable.api import KeySpec class ToolHistoryMixin(HasTraits): """ A mix-in class for tools to maintain a tool state history and to move backwards and forwards through that history stack. This mix-in listens for keypressed events; to handle keypresses in a subclass, call self._history_handle_key(event) to have this mix-in properly process the event. """ # Key to go to the original or start state in the history. reset_state_key = Instance(KeySpec, args=("Esc",)) # Key to go to the previous state in the history. prev_state_key = Instance(KeySpec, args=("Left", "control")) # Key to go to the next state in the history. next_state_key = Instance(KeySpec, args=("Right", "control")) # The state stack. _history = List # The current index into _history _history_index = Int #------------------------------------------------------------------------ # Abstract methods that subclasses must implement to handle keypresses #------------------------------------------------------------------------ def _next_state_pressed(self): """ Called when the tool needs to advance to the next state in the stack. The **_history_index** will have already been set to the index corresponding to the next state. """ pass def _prev_state_pressed(self): """ Called when the tool needs to advance to the previous state in the stack. The **_history_index** will have already been set to the index corresponding to the previous state. """ pass def _reset_state_pressed(self): """ Called when the tool needs to reset its history. The history index will have already been set to 0. """ pass #------------------------------------------------------------------------ # Protected methods for subclasses to use #------------------------------------------------------------------------ def _current_state(self): """ Returns the current history state. """ return self._history[self._history_index] def _reset_state(self, state): """ Clears the history stack and sets the first or original state in the history to *state*. """ self._history = [state] self._history_index = 0 return def _append_state(self, state, set_index=True): """ Clears the history after the current **_history_index**, and appends the given state to the history. If *set_index* is True, the method sets the **_history_index** to match the new, truncated history. If it is False, the history index is unchanged. """ new_history = self._history[:self._history_index+1] + [state] self._history = new_history if set_index: self._history_index = len(self._history) - 1 return def _pop_state(self): """ Pops the most last state off the history stack. If the history index points to the end of the stack, then it is adjusted; otherwise, the index is unaffected. If the stack is empty, the method raises an IndexError. Returns the popped state. """ if len(self._history) == 0: raise IndexError("Unable to pop empty history stack.") if self._history_index == len(self._history) - 1: self._history_index -= 1 return self._history.pop() #------------------------------------------------------------------------ # Private methods / event handlers #------------------------------------------------------------------------ def normal_key_pressed(self, event): """ Handles a key being pressed, and takes appropriate action if it is one of the history keys defined for this class. """ self._history_handle_key(event) return def _history_handle_key(self, event): if self.reset_state_key is not None and self.reset_state_key.match(event): self._history_index = 0 self._reset_state_pressed() event.handled = True elif self.prev_state_key is not None and self.prev_state_key.match(event): if self._history_index > 0: self._history_index -= 1 self._prev_state_pressed() event.handled = True elif self.next_state_key is not None and self.next_state_key.match(event): if self._history_index <= len(self._history) - 2: self._history_index += 1 self._next_state_pressed() event.handled = True else: return # EOF chaco-4.5.0/chaco/tools/tool_states.py0000644000076600000240000001147512426452312020515 0ustar jrocherstaff00000000000000from chaco.grid_mapper import GridMapper from traits.api import HasTraits class ToolState(HasTraits): def __init__(self, prev, next): self.prev = prev self.next = next def apply(self, tool): raise NotImplementedError() def revert(self, tool): raise NotImplementedError() class GroupedToolState(ToolState): def __init__(self, states): self.states = states def apply(self, tool): for state in self.states: state.apply(tool) def revert(self, tool): for state in self.states[::-1]: state.revert(tool) class PanState(ToolState): def apply(self, tool): if isinstance(tool.component.index_mapper, GridMapper): index_mapper = tool.component.index_mapper._xmapper value_mapper = tool.component.index_mapper._ymapper else: index_mapper = tool.component.index_mapper value_mapper = tool.component.value_mapper if self.next[0] != self.prev[0]: high = index_mapper.range.high low = index_mapper.range.low range = high-low index_mapper.range.high = self.next[0] + range/2 index_mapper.range.low = self.next[0] - range/2 if self.next[1] != self.prev[1]: high = value_mapper.range.high low = value_mapper.range.low range = high-low value_mapper.range.high = self.next[1] + range/2 value_mapper.range.low = self.next[1] - range/2 def revert(self, tool): if isinstance(tool.component.index_mapper, GridMapper): index_mapper = tool.component.index_mapper._xmapper value_mapper = tool.component.index_mapper._ymapper else: index_mapper = tool.component.index_mapper value_mapper = tool.component.value_mapper if self.next[0] != self.prev[0]: high = index_mapper.range.high low = index_mapper.range.low range = high-low index_mapper.range.high = self.prev[0] + range/2 index_mapper.range.low = self.prev[0] - range/2 if self.next[1] != self.prev[1]: high = value_mapper.range.high low = value_mapper.range.low range = high-low value_mapper.range.high = self.prev[1] + range/2 value_mapper.range.low = self.prev[1] - range/2 class ZoomState(ToolState): """ A zoom state which can be applied and reverted. This class exists so that subclasses can introduce new types of events which can be applied and reverted in the same manner. This greatly eases the code for managing history """ def apply(self, zoom_tool): index_factor = self.next[0]/self.prev[0] value_factor = self.next[1]/self.prev[1] if isinstance(zoom_tool.component.index_mapper, GridMapper): index_mapper = zoom_tool.component.index_mapper._xmapper value_mapper = zoom_tool.component.index_mapper._ymapper else: index_mapper = zoom_tool.component.index_mapper value_mapper = zoom_tool.component.value_mapper if index_factor != 1.0: zoom_tool._zoom_in_mapper(index_mapper, index_factor) if value_factor != 1.0: zoom_tool._zoom_in_mapper(value_mapper, value_factor) zoom_tool._index_factor = self.next[0] zoom_tool._value_factor = self.next[1] # TODO: Clip to domain bounds by inserting a pan tool and altering the # index factor and value factor def revert(self, zoom_tool): if isinstance(zoom_tool.component.index_mapper, GridMapper): index_mapper = zoom_tool.component.index_mapper._xmapper value_mapper = zoom_tool.component.index_mapper._ymapper else: index_mapper = zoom_tool.component.index_mapper value_mapper = zoom_tool.component.value_mapper zoom_tool._zoom_in_mapper(index_mapper, self.prev[0]/self.next[0]) zoom_tool._zoom_in_mapper(value_mapper, self.prev[1]/self.next[1]) zoom_tool._index_factor = self.prev[0] zoom_tool._value_factor = self.prev[1] class SelectedZoomState(ZoomState): def apply(self, zoom_tool): x_mapper = zoom_tool._get_x_mapper() y_mapper = zoom_tool._get_y_mapper() x_mapper.range.low = self.next[0] x_mapper.range.high = self.next[1] y_mapper.range.low = self.next[2] y_mapper.range.high = self.next[3] def revert(self, zoom_tool): x_mapper = zoom_tool._get_x_mapper() y_mapper = zoom_tool._get_y_mapper() x_mapper.range.low = self.prev[0] x_mapper.range.high = self.prev[1] y_mapper.range.low = self.prev[2] y_mapper.range.high = self.prev[3] chaco-4.5.0/chaco/tools/toolbars/0000755000076600000240000000000012426466422017427 5ustar jrocherstaff00000000000000chaco-4.5.0/chaco/tools/toolbars/__init__.py0000644000076600000240000000000012426452312021517 0ustar jrocherstaff00000000000000chaco-4.5.0/chaco/tools/toolbars/plot_toolbar.py0000644000076600000240000002436412426452312022503 0ustar jrocherstaff00000000000000from __future__ import with_statement import numpy from chaco.abstract_overlay import AbstractOverlay from chaco.tools.toolbars.toolbar_buttons import ToolbarButton, \ IndexAxisLogButton, ValueAxisLogButton, SaveAsButton, \ CopyToClipboardButton, ZoomResetButton, ExportDataToClipboardButton from enable.api import Container from enable.tools.api import HoverTool from traits.api import Bool, Float, on_trait_change, List, \ Tuple, Type, Enum class PlotToolbarHover(HoverTool): _last_xy = Tuple() def _is_in(self, x, y): return self.component.is_in(x, y) def normal_mouse_move(self, event): self._last_xy = (event.x, event.y) super(PlotToolbarHover, self).normal_mouse_move(event) def on_hover(self): """ This gets called when all the conditions of the hover action have been met, and the tool determines that the mouse is, in fact, hovering over a target region on the component. By default, this method call self.callback (if one is configured). """ for component in self.component.components: if component.is_in(*self._last_xy): self.callback(component.label) return self.callback('') class PlotToolbar(Container, AbstractOverlay): """ A toolbar for embedding buttons in """ buttons = List(Type(ToolbarButton)) # Should the toolbar be hidden hiding = Bool(True) # should the toolbar go automatically go back into hiding when the mouse # is not hovering over it auto_hide = Bool(True) # the radius used to determine how round to make the toolbar's edges end_radius = Float(4.0) # button spacing is defined as the number of pixels on either side of # a button. The gap between 2 buttons will be 2 x the button spacing button_spacing = Float(5.0) # how many pixels to put before and after the set of buttons horizontal_padding = Float(5.0) # how many pixels to put on top and bottom the set of buttons vertical_padding = Float(5.0) # The edge against which the toolbar is placed. location = Enum('top', 'right', 'bottom', 'left') # Should tooltips be shown? show_tooltips = Bool(False) ############################################################ # PlotToolbar API ############################################################ def __init__(self, component=None, *args, **kw): super(PlotToolbar, self).__init__(*args, **kw) self.component = component if component is not None and hasattr(component, 'toolbar_location'): self.location = component.toolbar_location for buttontype in self.buttons: self.add_button(buttontype()) hover_tool = PlotToolbarHover(component=self, callback=self.on_hover) self.tools.append(hover_tool) if self.location in ['top', 'bottom']: self._calculate_width() else: self._calculate_height() def _buttons_default(self): return [IndexAxisLogButton, ValueAxisLogButton, SaveAsButton, CopyToClipboardButton, ExportDataToClipboardButton, ZoomResetButton] def add_button(self, button): """ adds a button to the toolbar """ self.add(button) button.toolbar_overlay = self self._layout_needed = True return def normal_mouse_move(self, event): """ handler for normal mouse move """ self.on_hover('') if self.hiding: self.hiding = False def on_hover(self, tooltip): if self.show_tooltips: self.component.window.set_tooltip(tooltip) def normal_left_down(self, event): """ handler for a left mouse click """ if self.hiding: return else: for button in self.components: if button.is_in(event.x, event.y): button.perform(event) event.handled = True break ############################################################ # AbstractOverlay API ############################################################ def overlay(self, other_component, gc, view_bounds=None, mode="normal"): """ Draws this component overlaid on another component. """ starting_color = numpy.array([0.0, 1.0, 1.0, 1.0, 0.5]) ending_color = numpy.array([1.0, 0.0, 0.0, 0.0, 0.5]) x = self.x y = self.y height = self.height with gc: gc.begin_path() gc.move_to(x + self.end_radius, y) gc.arc_to(x + self.width, y, x + self.width, y + self.end_radius, self.end_radius) gc.arc_to(x + self.width, y + height, x + self.width - self.end_radius, y + height, self.end_radius) gc.arc_to(x, y + height, x, y + height - self.end_radius, self.end_radius) gc.arc_to(x, y, x + self.end_radius, y, self.end_radius) if self.location in ['top', 'bottom']: gc.linear_gradient(x, y, x, y + 100, numpy.array([starting_color, ending_color]), "pad") else: gc.linear_gradient(x, y, x + 100, y, numpy.array([starting_color, ending_color]), "pad") gc.draw_path() if not self.hiding: for button in self.components: button.draw(gc) def is_in(self, x, y): if (x >= self.x and x <= self.x2) and (y >= self.y and y <= self.y2): return True return False def _do_layout(self, component=None): if component is None: component = self.component if self.location in ['top', 'bottom']: if self.hiding: self.height = height = 10 else: tallest_button = max([button.height for button in self.components]) self.height = height = (tallest_button + self.vertical_padding * 2) else: if self.hiding: self.width = width = 10 else: widest_button = max([button.width for button in self.components]) self.width = width = (widest_button + self.horizontal_padding * 2) if component is not None: # Overlay positions are not relative to the component's position, # so we have to add in the component's position cx, cy = component.outer_position if self.location is 'top': self.x = (cx + (component.width - self.width) / 2 + component.padding_left) self.y = (cy + component.height + component.padding_bottom - height - 2) elif self.location is 'bottom': self.x = (cx + (component.width - self.width) / 2 + component.padding_left) self.y = cy + component.padding_bottom + 2 elif self.location is 'left': self.x = cx + component.padding_left + 2 self.y = (cy + (component.height - self.height) / 2 + component.padding_bottom) else: # 'right' self.x = (cx + component.width + component.padding_left - width - 2) self.y = (cy + (component.height - self.height) / 2 + component.padding_bottom) if self.location in ['top', 'bottom']: v_position = self.y + self.vertical_padding * 2 last_button_position = (self.x + self.horizontal_padding + self.button_spacing) for button in self.components: button.x = last_button_position button.y = v_position last_button_position += button.width + self.button_spacing * 2 else: # location is 'left' or 'right' h_position = self.x + self.horizontal_padding last_button_position = (self.y + self.vertical_padding + self.button_spacing) for button in reversed(self.components): h_offset = (self.width - button.width) / 2 button.y = last_button_position button.x = h_position + h_offset last_button_position += button.height + self.button_spacing * 2 def _dispatch_stateful_event(self, event, suffix): if self.is_in(event.x, event.y): if suffix == 'mouse_move': self.normal_mouse_move(event) elif suffix == 'left_down': self.normal_left_down(event) event.handled = True else: if self.auto_hide: self.hiding = True return ############################################################ # Trait handlers ############################################################ @on_trait_change('components, location') def _calculate_width(self): if self.location in ['top', 'bottom']: width = self.horizontal_padding * 2 for button in self.components: width += button.width + self.button_spacing * 2 self.width = max(10, width) self._layout_needed = True self.request_redraw() @on_trait_change('components, location') def _calculate_height(self): if self.location in ['left', 'right']: height = self.vertical_padding * 2 for button in self.components: height += button.height + self.button_spacing * 2 self.height = max(10, height) self._layout_needed = True self.request_redraw() @on_trait_change('hiding') def _hiding_changed(self): self._layout_needed = True self.request_redraw() @on_trait_change('auto_hide') def _auto_hide_changed(self): self.hiding = self.auto_hide self.request_redraw() chaco-4.5.0/chaco/tools/toolbars/toolbar_buttons.py0000644000076600000240000001731312426452312023217 0ustar jrocherstaff00000000000000import numpy from traits.etsconfig.api import ETSConfig from enable.tools.toolbars.toolbar_buttons import Button from chaco.tools.zoom_tool import ZoomTool from chaco.plot_graphics_context import PlotGraphicsContext from kiva.image import Image from pyface.image_resource import ImageResource from pyface.api import FileDialog, OK, error from traits.api import Instance, Str, Property, cached_property, \ List, Int, Enum class ToolbarButton(Button): image = Str() _image = Instance(Image) color = 'black' width = Property(Int, depends_on='label, image') height = Property(Int, depends_on='label, image') # bounds are used for hit testing bounds = Property(List, depends_on='label, image') def __init__(self, *args, **kw): super(ToolbarButton, self).__init__(*args, **kw) image_resource = ImageResource(self.image) self._image = Image(image_resource.absolute_path) @cached_property def _get_width(self): gc = PlotGraphicsContext((100, 100), dpi=72) gc.set_font(self.label_font) (w, h, descent, leading) = gc.get_full_text_extent(self.label) return max(self._image.width(), w) @cached_property def _get_height(self): gc = PlotGraphicsContext((100, 100), dpi=72) gc.set_font(self.label_font) (w, h, descent, leading) = gc.get_full_text_extent(self.label) return self._image.height() + h @cached_property def _get_bounds(self): return [self.width, self.height] def _draw_actual_button(self, gc): x_offset = self.x + (self.width - self._image.width()) / 2 gc.draw_image(self._image, (x_offset, self.y + 2, self._image.width(), self._image.height())) if self.label is not None and len(self.label) > 0: gc.set_font(self.label_font) (w, h, descent, leading) = gc.get_full_text_extent(self.label) if w < self.width: x_offset = self.x + (self.width - w) / 2 else: x_offset = self.x gc.set_text_position(x_offset, self.y - 8) gc.show_text(self.label) class IndexAxisLogButton(ToolbarButton): label = 'X Log Scale' tooltip = 'Change index axis scale' image = 'zoom-fit-width' def perform(self, event): if self.container.component.index_scale == 'linear': self.container.component.index_scale = 'log' else: self.container.component.index_scale = 'linear' self.container.request_redraw() return class ValueAxisLogButton(ToolbarButton): label = 'Y Log Scale' tooltip = 'Change value axis scale' image = 'zoom-fit-height' def perform(self, event): if self.container.component.value_scale == 'linear': self.container.component.value_scale = 'log' else: self.container.component.value_scale = 'linear' self.container.request_redraw() return class ZoomResetButton(ToolbarButton): label = 'Zoom Reset' tooltip = 'Zoom Reset' image = 'zoom-original' def perform(self, event): plot_component = self.container.component for overlay in plot_component.overlays: if isinstance(overlay, ZoomTool): overlay._reset_state_pressed() self.container.request_redraw() class SaveAsButton(ToolbarButton): label = 'Save As' tooltip = 'Save As' image = 'document-save' def perform(self, event): plot_component = self.container.component filter = 'PNG file (*.png)|*.png|\nTIFF file (*.tiff)|*.tiff|' dialog = FileDialog(action='save as', wildcard=filter) if dialog.open() != OK: return # Remove the toolbar before saving the plot, so the output doesn't # include the toolbar. plot_component.remove_toolbar() filename = dialog.path width, height = plot_component.outer_bounds gc = PlotGraphicsContext((width, height), dpi=72) gc.render_component(plot_component) try: gc.save(filename) except KeyError, e: errmsg = ("The filename must have an extension that matches " "a graphics format, such as '.png' or '.tiff'.") if str(e.message) != '': errmsg = ("Unknown filename extension: '%s'\n" % str(e.message)) + errmsg error(None, errmsg, title="Invalid Filename Extension") # Restore the toolbar. plot_component.add_toolbar() class CopyToClipboardButton(ToolbarButton): label = "Copy Image" tooltip = 'Copy to the clipboard' image = 'edit-copy' def perform(self, event): plot_component = self.container.component # Remove the toolbar before saving the plot, so the output doesn't # include the toolbar. plot_component.remove_toolbar() width, height = plot_component.outer_bounds gc = PlotGraphicsContext((width, height), dpi=72) gc.render_component(plot_component) if ETSConfig.toolkit == 'wx': self._perform_wx(width, height, gc) else: pass # Restore the toolbar. plot_component.add_toolbar() def _perform_wx(self, width, height, gc): import wx bitmap = wx.BitmapFromBufferRGBA(width + 1, height + 1, gc.bmp_array.flatten()) data = wx.BitmapDataObject() data.SetBitmap(bitmap) if wx.TheClipboard.Open(): wx.TheClipboard.SetData(data) wx.TheClipboard.Close() else: wx.MessageBox("Unable to open the clipboard.", "Error") class ExportDataToClipboardButton(ToolbarButton): label = "Copy Data" tooltip = 'Copy data to the clipboard' image = 'application-vnd-ms-excel' orientation = Enum('v', 'h') def perform(self, event): if ETSConfig.toolkit == 'wx': self._perform_wx() elif ETSConfig.toolkit == 'qt4': self._perform_qt() else: pass def _get_data_from_plots(self): values = [] indices = [] for renderers in self.container.component.plots.values(): for renderer in renderers: indices.append(renderer.index.get_data()) values.append(renderer.value.get_data()) return indices, values def _serialize_data(self, indices, values): # if all of rows are the same length, use faster algorithms, # otherwise go element by element adding the necessary empty strings if len(set([len(l) for l in values])) == 1: data = [indices[0]] + values if self.orientation == 'v': data = numpy.array(data).T.tolist() data_str = '' for row in data: data_str += ','.join(['%f' % v for v in row]) + '\n' return data_str else: # There might not be a single solution which fits all cases, # so this is left to specific implementations to override raise NotImplementedError() def _perform_wx(self): import wx indices, values = self._get_data_from_plots() data_str = self._serialize_data(indices, values) data_obj = wx.TextDataObject(data_str) if wx.TheClipboard.Open(): wx.TheClipboard.SetData(data_obj) wx.TheClipboard.Close() else: wx.MessageBox("Unable to open the clipboard.", "Error") def _perform_qt(self): from pyface.qt import QtGui indices, values = self._get_data_from_plots() data_str = self._serialize_data(indices, values) QtGui.QApplication.clipboard().setText(data_str)chaco-4.5.0/chaco/tools/tracking_pan_tool.py0000644000076600000240000000370312426452312021645 0ustar jrocherstaff00000000000000""" Defines the TrackingPanTool class. """ # Chaco imports from chaco.tools.api import PanTool class TrackingPanTool(PanTool): """ Allows the user to pan around a plot. The user clicks a mouse button and drags to pan; the tool then returns to a tracking state. """ def _end_pan(self, event): plot = self.component xrange = plot.x_mapper.range yrange = plot.y_mapper.range if not self.constrain or self.constrain_direction == "x": high = xrange.high low = xrange.low if xrange.default_state == 'low_track': hi_val = max([source.get_bounds()[1] for source in xrange.sources]) if hi_val >= low and hi_val <= high: xrange.set_bounds('track','auto') elif xrange.default_state == 'high_track': lo_val = min([source.get_bounds()[0] for source in xrange.sources]) if lo_val >= low and lo_val <= high: xrange.set_bounds('auto','track') if not self.constrain or self.constrain_direction == "y": high = yrange.high low = yrange.low if yrange.default_state == 'low_track': hi_val = max([source.get_bounds()[1] for source in yrange.sources]) if hi_val >= low and hi_val <= high: yrange.set_bounds('track','auto') elif yrange.default_state == 'high_track': lo_val = min([source.get_bounds()[0] for source in yrange.sources]) if lo_val >= low and lo_val <= high: yrange.set_bounds('auto','track') if self._auto_constrain: self.constrain = False self.constrain_direction = None self.event_state = "normal" event.window.set_pointer("arrow") if event.window.mouse_owner == self: event.window.set_mouse_owner(None) event.handled = True return # EOF chaco-4.5.0/chaco/tools/tracking_zoom.py0000644000076600000240000000620312426452312021014 0ustar jrocherstaff00000000000000""" Defines the TrackingZoom class. """ # Chaco imports from zoom_tool import ZoomTool class TrackingZoom(ZoomTool): """ Allows the user to zoom in or out on a plot that is using tracking. The **default_state** of the data range determines the tracking behavior. For example, if the data range's **default_state** is "low_track", the range's high value snaps to the right edge and the tracking, low, value follows it by the data range's **tracking_amount** value (and vice versa for "high_track"). """ def normal_mouse_wheel(self, event): """ Handles the mouse wheel being used when the tool is in the 'normal' state. Overrides ZoomTool """ if self.enable_wheel and event.mouse_wheel != 0: if event.mouse_wheel > 0: # zoom in zoom = 1.0 / (1.0 + 0.5 * self.wheel_zoom_step) elif event.mouse_wheel < 0: # zoom out zoom = 1.0 + 0.5 * self.wheel_zoom_step # We'll determine the current position of the cursor in dataspace, # then zoom in while trying to maintain the mouse screen coordinates # in the new range. c = self.component low_pt, high_pt = self._map_coordinate_box((c.x, c.y), (c.x2, c.y2)) mouse_pos = (c.x_mapper.map_data(event.x), c.y_mapper.map_data(event.y)) if self.tool_mode == "range": datarange_list = [(self._determine_axis(), self._get_mapper().range)] else: datarange_list = [(0, c.x_mapper.range), (1, c.y_mapper.range)] orig_low, orig_high = self._history[0] for ndx, datarange in datarange_list: mouse_val = mouse_pos[ndx] newlow = mouse_val - zoom * (mouse_val - low_pt[ndx]) newhigh = mouse_val + zoom * (high_pt[ndx] - mouse_val) if type(orig_high) in (tuple,list): ol, oh = orig_low[ndx], orig_high[ndx] else: ol, oh = orig_low, orig_high if self._zoom_limit_reached(ol, oh, newlow, newhigh): event.handled = True return if datarange.default_state == 'low_track': hi = max([source.get_bounds()[1] for source in datarange.sources]) #is hi in the current view? if hi >= low_pt[ndx] and hi <= high_pt[ndx]: datarange.scale_tracking_amount(zoom) newhigh = 'auto' newlow = 'track' elif datarange.default_state == 'high_track': lo = min([source.get_bounds()[0] for source in datarange.sources]) #is lo in the current view? if lo >= low_pt[ndx] and lo <= high_pt[ndx]: datarange.scale_tracking_amount(zoom) newlow = 'auto' newhigh = 'track' datarange.set_bounds(newlow, newhigh) event.handled = True self.component.request_redraw() return chaco-4.5.0/chaco/tools/traits_tool.py0000755000076600000240000001047612426452312020523 0ustar jrocherstaff00000000000000""" Defines the TraitsTool and Fifo classes, and get_nested_components90 function. """ # Enthought library imports from enable.api import BaseTool, Container from traits.api import List, Dict, Str # Chaco imports from chaco.api import PlotAxis, ColorBar class Fifo(object): """ Slightly-modified version of the Fifo class from the Python cookbook: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/68436 """ def __init__(self): self.nextin = 0 self.nextout = 0 self.data = {} def append(self, value): self.data[self.nextin] = value self.nextin += 1 def extend(self, values): if len(values) > 0: for i,val in enumerate(values): self.data[i+self.nextin] = val self.nextin += i+1 def isempty(self): return self.nextout >= self.nextin def pop(self): value = self.data[self.nextout] del self.data[self.nextout] self.nextout += 1 return value def get_nested_components(container, classes): """ Returns a list of fundamental plotting components from a container with nested containers. Performs a breadth-first search of the containment hierarchy. Each element in the returned list is a tuple (component, (x,y)) where (x,y) is the coordinate frame offset of the component from the top-level container. """ components = [] worklist = Fifo() worklist.append((container, (0,0))) while 1: item, offset = worklist.pop() if isinstance(item, Container): new_offset = (offset[0]+item.x, offset[1]+item.y) for c in item.components: worklist.append((c, new_offset)) for overlay in item.overlays + item.underlays: components.append((overlay, offset)) elif any([isinstance(item, klass) for klass in classes]): components.append((item, offset)) for overlay in item.overlays + item.underlays: components.append((overlay, offset)) if worklist.isempty(): break return components class TraitsTool(BaseTool): """ Tool to edit the traits of plots, grids, and axes. """ # This tool does not have a visual representation (overrides BaseTool). draw_mode = "none" # This tool is not visible (overrides BaseTool). visible = False # The classes of components that should trigger a traits view classes = List([PlotAxis, ColorBar]) # A dict of Class : View providing alternate views for a particular component views = Dict # The event to trigger the edit on event = Str('left_dclick') def _dispatch_stateful_event(self, event, suffix): """If the event type matches the specification in *event*, look for a component that matches one of the classes in *classes* in our containment hierarchy. If one is found, edit it using either the default editor, or an alternate editor specified in *views* """ if suffix != self.event: return x = event.x y = event.y # First determine what component or components we are going to hittest # on. If our component is an Axis or PlotRenderer of any sort, # then that is the only candidate. If our component is a container, # then we add its non-container components to the list of candidates; # any nested containers are lower priority than primary plot components. candidates = get_nested_components(self.component, [Container] + self.classes) # Hittest against all the candidate and take the first one item = None for candidate, offset in candidates: if candidate.is_in(x-offset[0], y-offset[1]): item=candidate break if item is not None: self.component.active_tool = self if item.__class__ in self.views: item.edit_traits(kind="livemodal", view=self.views[item.__class__], parent=event.window.control) else: item.edit_traits(kind="livemodal", parent=event.window.control) event.handled = True self.component.active_tool = None item.request_redraw() return # EOF chaco-4.5.0/chaco/tools/zoom_tool.py0000644000076600000240000000010212426452312020157 0ustar jrocherstaff00000000000000from better_selecting_zoom import BetterSelectingZoom as ZoomTool chaco-4.5.0/chaco/tooltip.py0000644000076600000240000001321012426452312016474 0ustar jrocherstaff00000000000000""" Defines the ToolTip class. """ from __future__ import with_statement from numpy import array # Enthought library imports from enable.api import black_color_trait, white_color_trait from enable.font_metrics_provider import font_metrics_provider from kiva.trait_defs.kiva_font_trait import KivaFont from traits.api import Any, Bool, List, Int, Float, on_trait_change # Local imports from abstract_overlay import AbstractOverlay from plot_component import PlotComponent from label import Label class ToolTip(AbstractOverlay): """ An overlay that is a toolip. """ # The font to render the tooltip. font = KivaFont('modern 10') # The color of the text in the tooltip text_color = black_color_trait # The ammount of space between the border and the text. border_padding = Int(4) # The number of pixels between lines. line_spacing = Int(4) # List of text strings to put in the tooltip. lines = List # Angle to rotate (counterclockwise) in degrees. NB this will *only* # currently affect text, so probably only useful if borders and background # are disabled rotate_angle = Float(0.0) # Should the tooltip automatically reposition itself to remain visible # and unclipped on its overlaid component? auto_adjust = Bool(True) # The tooltip is a fixed size. (Overrides PlotComponent.) resizable = "" # Use a visible border. (Overrides Enable Component.) border_visible = True # Use a white background color (overrides AbstractOverlay). bgcolor = white_color_trait #---------------------------------------------------------------------- # Private Traits #---------------------------------------------------------------------- _font_metrics_provider = Any() _text_props_valid = Bool(False) _max_line_width = Float(0.0) _total_line_height = Float(0.0) def draw(self, gc, view_bounds=None, mode='normal'): """ Draws the plot component. Overrides PlotComponent. """ self.overlay(self, gc, view_bounds=view_bounds, mode='normal') return def overlay(self, component, gc, view_bounds=None, mode='normal'): """ Draws the tooltip overlaid on another component. Overrides AbstractOverlay. """ self.do_layout() PlotComponent._draw(self, gc, view_bounds, mode) return def _draw_overlay(self, gc, view_bounds=None, mode='normal'): """ Draws the overlay layer of a component. Overrides PlotComponent. """ with gc: edge_space = self.border_width + self.border_padding gc.translate_ctm(self.x + edge_space, self.y) y = self.height - edge_space for i, label in enumerate(self._cached_labels): label_height = self._cached_line_sizes[i][1] y -= label_height gc.translate_ctm(0,y) label.draw(gc) gc.translate_ctm(0,-y) y -= self.line_spacing return def _do_layout(self): """Computes the size of the tooltip, and creates the label objects for each line. Overrides PlotComponent. """ if not self._text_props_valid: self._recompute_text() outer_bounds = [self._max_line_width + 2*self.border_padding + self.hpadding, self._total_line_height + 2*self.border_padding + self.vpadding] self.outer_bounds = outer_bounds if self.auto_adjust and self.component is not None: new_pos = list(self.outer_position) for dimindex in (0,1): pos = self.position[dimindex] extent = outer_bounds[dimindex] c_min = self.component.position[dimindex] c_max = c_min + self.component.bounds[dimindex] # Is the tooltip just too wide/tall? if extent > (c_max - c_min): new_pos[dimindex] = c_min # Does it extend over the c_max edge? (right/top) elif pos + extent > c_max: new_pos[dimindex] = c_max - extent # Does it extend over the c_min edge? This is not an elif so # that we can fix the situation where the c_max edge adjustment # above pushes the position negative. if new_pos[dimindex] < c_min: new_pos[dimindex] = c_min self.outer_position = new_pos self._layout_needed = False def _recompute_text(self): labels = [Label(text=line, font=self.font, margin=0, bgcolor='transparent', border_width=0, color=self.text_color, rotate_angle=self.rotate_angle) for line in self.lines] dummy_gc = self._font_metrics_provider line_sizes = array([label.get_width_height(dummy_gc) for label in labels]) self._cached_labels = labels self._cached_line_sizes = line_sizes self._max_line_width = max(line_sizes[:,0]) self._total_line_height = sum(line_sizes[:,1]) + \ len(line_sizes-1)*self.line_spacing self._layout_needed = True return def __font_metrics_provider_default(self): return font_metrics_provider() @on_trait_change("font,text_color,lines,lines_items") def _invalidate_text_props(self): self._text_props_valid = False self._layout_needed = True @on_trait_change("border_padding,line_spacing,lines,lines_items,padding") def _invalidate_layout(self): self._layout_needed = True self.request_redraw() chaco-4.5.0/chaco/transform_color_mapper.py0000644000076600000240000001574712426452312021600 0ustar jrocherstaff00000000000000from numpy import clip, isinf, ones_like, empty from chaco.api import ColorMapper from traits.api import Trait, Callable, Tuple, Float, on_trait_change from speedups import map_colors, map_colors_uint8 class TransformColorMapper(ColorMapper): """This class adds arbitrary data transformations to a ColorMapper. The default ColorMapper is basically a linear mapper from data space to color space. A TransformColorMapper allows a nonlinear mapper to be created. A ColorMapper works by linearly transforming the data from data space to the unit interval [0,1], and then linearly mapping that interval to the color space. A TransformColorMapper allows an arbitrary transform to be inserted at two places in this process. First, an initial transformation, `data_func` can be applied to the data *before* is it mapped to [0,1]. Then another function, `unit_func`, can be applied to the transformed data on [0,1] before it is mapped to color space. Normally, a `unit_func` is map of the unit interval [0,1] to itself (e.g. x^2 or sin(pi*x/2)). """ data_func = Trait(None, None, Callable) unit_func = Trait(None, None, Callable) transformed_bounds = Tuple(Trait(None, None, Float), Trait(None, None, Float)) #------------------------------------------------------------------- # Trait handlers #------------------------------------------------------------------- @on_trait_change('data_func, range.updated') def _update_transformed_bounds(self): if self.range is None: # The ColorMapper doesn't have a range yet, so don't do anything. # This apparently occurs during initialization. return if self.data_func is not None: low = self.range.low high = self.range.high trans_low = self.data_func(low) trans_high = self.data_func(high) self.transformed_bounds = (trans_low, trans_high) else: self.transformed_bounds = (None, None) self.updated = True def _unit_func_changed(self): self.updated = True #------------------------------------------------------------------- # Class methods #------------------------------------------------------------------- @classmethod def from_color_mapper(cls, color_mapper, data_func=None, unit_func=None, **traits): """ Create a TransformColorMapper from an existing ColorMapper instance. """ segdata = color_mapper._segmentdata return cls.from_segment_map(segdata, range=color_mapper.range, data_func=data_func, unit_func=unit_func, **traits) @classmethod def from_color_map(cls, color_map, data_func=None, unit_func=None, **traits): """Create a TransformColorMapper from a colormap generator function. The return value is an instance of TransformColorMapper, *not* a factory function, so this does not provide a direct replacement for a standard colormap factory function. For that, use the class method TransoformColorMapper.factory_from_color_map(). """ # Call the colormap factory function to create an instance of a # ColorMapper. color_mapper = color_map(None, **traits) segdata = color_mapper._segmentdata return cls.from_segment_map(segdata, range=color_mapper.range, data_func=data_func, unit_func=unit_func, **traits) @classmethod def factory_from_color_map(cls, color_map, data_func=None, unit_func=None, **traits): """ Create a TransformColorMapper factory function from a standard colormap factory function. WARNING: This function is untested; I realized I didn't need it shortly after writing it, so I haven't tried it yet. --WW """ # Call the colormap factory function to create an instance of a # ColorMapper. color_mapper = color_map(None, **traits) def factory(range, **traits): tcm = cls.from_color_mapper(color_mapper, data_func=data_func, unit_func=unit_func, **traits) return tcm return factory #------------------------------------------------------------------- # ColorMapper interface (these override methods from ColorMapper) #------------------------------------------------------------------- def map_screen(self, data_array): """ Maps an array of data values to an array of colors. """ norm_data = self._compute_normalized_data(data_array) # The data are normalized, so we can pass low = 0, high = 1 rgba = map_colors(norm_data, self.steps, 0, 1, self._red_lut, self._green_lut, self._blue_lut, self._alpha_lut) return rgba def map_index(self, data_array): """ Maps an array of values to their corresponding color band index. """ norm_data = self._compute_normalized_data(data_array) indices = (norm_data * (self.steps-1)).astype(int) return indices def map_uint8(self, data_array): """ Maps an array of data values to an array of colors. """ norm_data = self._compute_normalized_data(data_array) rgba = map_colors_uint8(norm_data, self.steps, 0.0, 1.0, self._red_lut_uint8, self._green_lut_uint8, self._blue_lut_uint8, self._alpha_lut_uint8) return rgba #------------------------------------------------------------------- # Private methods #------------------------------------------------------------------- def _compute_normalized_data(self, data_array): """ Apply `data_func`, then linearly scale to the unit interval, and then apply `unit_func`. """ # FIXME: Deal with nans? if self._dirty: self._recalculate() if self.data_func is not None: data_array = self.data_func(data_array) low, high = self.transformed_bounds else: low, high = self.range.low, self.range.high range_diff = high - low # Linearly transform the values to the unit interval. if range_diff == 0.0 or isinf(range_diff): # Handle null range, or infinite range (which can happen during # initialization before range is connected to a data source). norm_data = 0.5*ones_like(data_array) else: norm_data = empty(data_array.shape, dtype='float32') norm_data[:] = data_array norm_data -= low norm_data /= range_diff clip(norm_data, 0.0, 1.0, norm_data) if self.unit_func is not None: norm_data = self.unit_func(norm_data) return norm_data chaco-4.5.0/chaco/ui/0000755000076600000240000000000012426466422015057 5ustar jrocherstaff00000000000000chaco-4.5.0/chaco/ui/__init__.py0000644000076600000240000000000012426452312017147 0ustar jrocherstaff00000000000000chaco-4.5.0/chaco/ui/axis_ui.py0000644000076600000240000001250512426452312017066 0ustar jrocherstaff00000000000000""" This file contains the ui specifications for the chaco.axis.Axis objects. Much of it is defined in re-usable chunks so that elements of it can be used in UIs of objects that contain an axis. """ # Enthought Imports from traitsui.api import View, Group, VGroup, HGroup, Item, TextEditor title_group = Group( Item("title", label="Text", editor=TextEditor()), # Fix me: We really don't have an reasonable font editor. #Item("title_font", label="Font", style="custom"), Item("title_color", label="Color", style="simple"), ) axis_line_group = Group( Item("axis_line_visible", label="Visible"), Group( Item("axis_line_color", label="Color", style="simple"), Item("axis_line_weight", label="Thickness"), # Line Style enabled_when='object.axis_line_visible==True', ), ) tick_labels_group = Group( # fix me: We need a 'Visible' trait on that determines # whether tick labels are visible or not. # Visible -- The rest should be in a group that is enabled # by this. # Fix me: Need an reasonable font editor. #Item("tick_label_font", label="Font"), Item("tick_label_color", label="Color", style="simple"), # Fix me: set the rotation of the label. # Rotation # Fix me: Set the offset (in pixels?) of the label to # allow people to "bump" them up or down. # Offset # Fix me: Are labels next to the axis or off the side of the # plot? # relative_to: axis|plot_min|plot_max ) tick_lines_group = Group( Item("tick_visible", label="Visible"), Group( # These are the only non-axis part of the view... HGroup( # fix me: THe enabled_when is not working # correctly. This failure began # when we switched to using context. Item("tick_interval_ui", label="Interval", enabled_when = "object.tick_interval_auto_ui == False"), Item("tick_interval_auto_ui", label="Auto"), ), Item("tick_color", label="Color", style="simple"), Item("tick_weight", label="Thickness"), #HGroup( Item("tick_in", label="Tick in (pixels)"), Item("tick_out", label="Tick out (pixels)"), #), enabled_when="object.tick_visible==True", ), ) tick_lines_group = Group( Item("tick_visible", label="Visible"), Group( Item("tick_color", label="Color", style="simple"), Item("tick_weight", label="Thickness"), Item("tick_in", label="Tick in (pixels)"), Item("tick_out", label="Tick out (pixels)"), # Fix me: We really need to split out the tick interval # into a UI like this. #HGroup( # Item("tick_interval_ui", label="Interval", # enabled_when = "object.tick_interval_auto_ui == False"), # Item("tick_interval_auto_ui", label="Auto"), #), Item(label="Note: Tick Interval not currently settable."), enabled_when="object.tick_visible==True", ), ) # We are missing a group to specify the "scale" or "range" setting # The main view for an axis... default_view = View( VGroup(Group(title_group, label='Title', show_border=True), Group(axis_line_group, label='Axis Line', show_border=True), HGroup( Group(tick_lines_group, label='Tick Lines', show_border=True), Group(tick_labels_group, label='Labels', show_border=True), label='Ticks', ), layout="tabbed", ), buttons = ["OK", "Cancel"], ) # Fix me: Should we do something here where we register this with the Axis object? chaco-4.5.0/chaco/ui/plot_window.py0000644000076600000240000000113612426452312017770 0ustar jrocherstaff00000000000000# Enthought library imports from traits.api import Instance, HasTraits from traitsui.api import View, Item from enable.api import Container from enable.component_editor import ComponentEditor class PlotWindow(HasTraits): plot = Instance(Container) traits_view = View(Item('plot', editor=ComponentEditor(), height=300, width=500, show_label=False, ), title='Chaco Plot', resizable=True ) chaco-4.5.0/chaco/ui/popupable_plot.py0000644000076600000240000000220012426452312020441 0ustar jrocherstaff00000000000000# Enthought library imports from traits.api import List from chaco.api import VPlotContainer from chaco.plot import Plot from chaco.tools.api import PanTool, ZoomTool from chaco.ui.plot_window import PlotWindow from traitsui.wx.constants import WindowColor class PopupablePlot(Plot): """A Plot class that pops up in a new window on double click""" # FIXME: It would be nice to queue up other types of commands and settings command_queue = List() def normal_left_dclick(self, event): plot = Plot(self.data) for data, kw in self.command_queue: plot.plot(data, **kw) plot.title = self.title plot.title = self.title container = VPlotContainer(bgcolor=WindowColor) container.add(plot) plot.tools.append(PanTool(plot)) plot.overlays.append(ZoomTool(plot)) window = PlotWindow(plot=container) window.edit_traits(kind='live', parent=event.window.control) def plot(self, data, **kw): """Queue up the plot commands""" self.command_queue.append((data, kw)) super(PopupablePlot, self).plot(data, **kw) return chaco-4.5.0/chaco/variable_size_scatterplot.py0000644000076600000240000000041412426452312022247 0ustar jrocherstaff00000000000000""" The base ScatterPlot class now accepts variable sized markers. This definition remains for backwards compatibility. """ from chaco.scatterplot import ScatterPlot # TODO: This should be officially deprecated. class VariableSizeScatterPlot(ScatterPlot): pass chaco-4.5.0/chaco.egg-info/0000755000076600000240000000000012426466422016134 5ustar jrocherstaff00000000000000chaco-4.5.0/chaco.egg-info/dependency_links.txt0000644000076600000240000000000112426466422022202 0ustar jrocherstaff00000000000000 chaco-4.5.0/chaco.egg-info/not-zip-safe0000644000076600000240000000000112426466422020362 0ustar jrocherstaff00000000000000 chaco-4.5.0/chaco.egg-info/PKG-INFO0000644000076600000240000000705612426466422017241 0ustar jrocherstaff00000000000000Metadata-Version: 1.1 Name: chaco Version: 4.5.0 Summary: interactive 2-dimensional plotting Home-page: http://code.enthought.com/projects/chaco Author: ETS Developers Author-email: enthought-dev@enthought.com License: BSD Download-URL: http://www.enthought.com/repo/ets/chaco-4.5.0.tar.gz Description: ========================================= chaco: interactive 2-dimensional plotting ========================================= http://github.enthought.com/chaco .. image:: https://api.travis-ci.org/enthought/chaco.png?branch=master :target: https://travis-ci.org/enthought/chaco :alt: Build status .. image:: https://coveralls.io/repos/enthought/chaco/badge.png?branch=feature%2Fcoverall_badge :target: https://coveralls.io/r/enthought/chaco?branch=feature%2Fcoverall_badge :alt: Test coverage Chaco is a Python plotting application toolkit that facilitates writing plotting applications at all levels of complexity, from simple scripts with hard-coded data to large plotting programs with complex data interrelationships and a multitude of interactive tools. While Chaco generates attractive static plots for publication and presentation, it also works well for interactive data visualization and exploration. Features -------- - **Flexible drawing and layout**: Plots consist of graphical components which can be placed inside nestable containers for layout, positioning, and event dispatch. Every component has a configurable rendering loop with distinct layers and backbuffering. Containers can draw cooperatively so that layers span across the containment hierarchy. - **Modular and extensible architecture**: Chaco is object-oriented from the ground up for ease of extension and customization. There are clear interfaces and abstract classes defining extension points for writing your own custom behaviors, from custom tools, plot types, layouts, etc. Most classes are also "subclass-friendly", so that subclasses can override one or two methods and everything else just works. - **Data model for ease of extension and embedding**: Chaco separates the data from any transformations of the data that are needed for displaying it. This separation makes it easier to extend Chaco, or embed it in applications. Prerequisites ------------- You must have the following libraries installed before building or installing Chaco: * `Numpy `_ * `distribute `_ * `enable `_ Platform: Windows Platform: Linux Platform: Mac OS-X Platform: Unix Platform: Solaris Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Science/Research Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: MacOS Classifier: Operating System :: Microsoft :: Windows Classifier: Operating System :: OS Independent Classifier: Operating System :: POSIX Classifier: Operating System :: Unix Classifier: Programming Language :: C Classifier: Programming Language :: Python Classifier: Topic :: Scientific/Engineering Classifier: Topic :: Software Development Classifier: Topic :: Software Development :: Libraries chaco-4.5.0/chaco.egg-info/requires.txt0000644000076600000240000000000612426466422020530 0ustar jrocherstaff00000000000000enablechaco-4.5.0/chaco.egg-info/SOURCES.txt0000644000076600000240000001376712426466422020036 0ustar jrocherstaff00000000000000MANIFEST.in README.rst setup.py chaco/__init__.py chaco/_cython_speedups.c chaco/_isnan.h chaco/_speedups_fallback.py chaco/abstract_colormap.py chaco/abstract_controller.py chaco/abstract_data_range.py chaco/abstract_data_source.py chaco/abstract_mapper.py chaco/abstract_overlay.py chaco/abstract_plot_data.py chaco/abstract_plot_renderer.py chaco/api.py chaco/array_data_source.py chaco/array_plot_data.py chaco/axis.py chaco/axis_view.py chaco/barplot.py chaco/base.py chaco/base_1d_mapper.py chaco/base_2d_plot.py chaco/base_candle_plot.py chaco/base_contour_plot.py chaco/base_data_range.py chaco/base_plot_container.py chaco/base_plot_frame.py chaco/base_xy_plot.py chaco/candle_plot.py chaco/chaco_plot_container_editor.py chaco/chaco_plot_editor.py chaco/chaco_traits.py chaco/chaco_version.py chaco/cmap_image_plot.py chaco/color_bar.py chaco/color_mapper.py chaco/color_spaces.py chaco/colormap_generators.py chaco/colormapped_scatterplot.py chaco/colormapped_selection_overlay.py chaco/contour_line_plot.py chaco/contour_poly_plot.py chaco/cross_plot_frame.py chaco/data_label.py chaco/data_range_1d.py chaco/data_range_2d.py chaco/data_view.py chaco/datamapper.py chaco/default_colormaps.py chaco/default_colors.py chaco/errorbar_plot.py chaco/example_support.py chaco/filled_line_plot.py chaco/function_data_source.py chaco/function_image_data.py chaco/grid.py chaco/grid_data_source.py chaco/grid_mapper.py chaco/horizon_plot.py chaco/image_data.py chaco/image_plot.py chaco/jitterplot.py chaco/label.py chaco/label_axis.py chaco/lasso_overlay.py chaco/legend.py chaco/linear_mapper.py chaco/lineplot.py chaco/log_mapper.py chaco/multi_array_data_source.py chaco/multi_line_plot.py chaco/pdf_graphics_context.py chaco/plot.py chaco/plot_canvas.py chaco/plot_canvas_toolbar.py chaco/plot_component.py chaco/plot_containers.py chaco/plot_factory.py chaco/plot_graphics_context.py chaco/plot_label.py chaco/plot_template.py chaco/plotscrollbar.py chaco/point_data_source.py chaco/polar_line_renderer.py chaco/polar_mapper.py chaco/polygon_plot.py chaco/quiverplot.py chaco/scales_tick_generator.py chaco/scatter_inspector_overlay.py chaco/scatter_markers.py chaco/scatterplot.py chaco/selectable_legend.py chaco/selectable_overlay_container.py chaco/serializable.py chaco/simple_plot_frame.py chaco/speedups.py chaco/subdivision_cells.py chaco/subdivision_mapper.py chaco/svg_graphics_context.py chaco/text_box_overlay.py chaco/ticks.py chaco/toolbar_plot.py chaco/tooltip.py chaco/transform_color_mapper.py chaco/variable_size_scatterplot.py chaco.egg-info/PKG-INFO chaco.egg-info/SOURCES.txt chaco.egg-info/dependency_links.txt chaco.egg-info/not-zip-safe chaco.egg-info/requires.txt chaco.egg-info/top_level.txt chaco/contour/__init__.py chaco/contour/cntr.c chaco/contour/setup.py chaco/layers/__init__.py chaco/layers/api.py chaco/layers/status_layer.py chaco/layers/svg_range_selection_overlay.py chaco/overlays/__init__.py chaco/overlays/aligned_container_overlay.py chaco/overlays/api.py chaco/overlays/container_overlay.py chaco/overlays/coordinate_line_overlay.py chaco/overlays/databox.py chaco/overlays/simple_inspector_overlay.py chaco/overlays/text_grid_overlay.py chaco/plugin/__init__.py chaco/plugin/chaco_plugin.py chaco/plugin/plot_editor.py chaco/plugin/workbench_session.py chaco/scales/__init__.py chaco/scales/api.py chaco/scales/formatters.py chaco/scales/safetime.py chaco/scales/scales.py chaco/scales/scales_test_case.py chaco/scales/time_scale.py chaco/scales/time_scale_test_case.py chaco/shell/__init__.py chaco/shell/chaco_shell_error.py chaco/shell/commands.py chaco/shell/plot_maker.py chaco/shell/plot_window.py chaco/shell/preferences.py chaco/shell/scaly_plot.py chaco/shell/session.py chaco/tests/__init__.py chaco/tests/_tools.py chaco/tests/array_plot_data_test_case.py chaco/tests/arraydatasource_test_case.py chaco/tests/base_utils_test_case.py chaco/tests/border_test_case.py chaco/tests/colormapper_test_case.py chaco/tests/component_tests.py chaco/tests/data_view_test_case.py chaco/tests/datarange_1d_test_case.py chaco/tests/datarange_2d_test_case.py chaco/tests/default_colormaps_test_case.py chaco/tests/grid_data_source_test_case.py chaco/tests/grid_mapper_test_case.py chaco/tests/hittest_test_case.py chaco/tests/instantiation_order_test_case.py chaco/tests/linearmapper_test_case.py chaco/tests/logmapper_test_case.py chaco/tests/plot_test_case.py chaco/tests/plotcontainer_test_case.py chaco/tests/scatterplot_renderers_test_case.py chaco/tests/serializable_base.py chaco/tests/serializable_test_case.py chaco/tests/speedups_test_case.py chaco/tests/test_cmap_image_plot.py chaco/tests/test_colormapped_scatterplot.py chaco/tools/__init__.py chaco/tools/api.py chaco/tools/base_zoom_tool.py chaco/tools/better_selecting_zoom.py chaco/tools/better_zoom.py chaco/tools/broadcaster.py chaco/tools/cursor_tool.py chaco/tools/data_label_tool.py chaco/tools/dataprinter.py chaco/tools/drag_tool.py chaco/tools/drag_zoom.py chaco/tools/draw_points_tool.py chaco/tools/highlight_tool.py chaco/tools/image_inspector_tool.py chaco/tools/lasso_selection.py chaco/tools/legend_highlighter.py chaco/tools/legend_tool.py chaco/tools/line_inspector.py chaco/tools/line_segment_tool.py chaco/tools/move_tool.py chaco/tools/pan_tool.py chaco/tools/pan_tool2.py chaco/tools/point_marker.py chaco/tools/range_selection.py chaco/tools/range_selection_2d.py chaco/tools/range_selection_overlay.py chaco/tools/rect_zoom.py chaco/tools/regression_lasso.py chaco/tools/save_tool.py chaco/tools/scatter_inspector.py chaco/tools/select_tool.py chaco/tools/simple_inspector.py chaco/tools/simple_zoom.py chaco/tools/tool_history_mixin.py chaco/tools/tool_states.py chaco/tools/tracking_pan_tool.py chaco/tools/tracking_zoom.py chaco/tools/traits_tool.py chaco/tools/zoom_tool.py chaco/tools/tests/__init__.py chaco/tools/tests/pan_tool_test_case.py chaco/tools/tests/range_selection_test_case.py chaco/tools/toolbars/__init__.py chaco/tools/toolbars/plot_toolbar.py chaco/tools/toolbars/toolbar_buttons.py chaco/ui/__init__.py chaco/ui/axis_ui.py chaco/ui/plot_window.py chaco/ui/popupable_plot.pychaco-4.5.0/chaco.egg-info/top_level.txt0000644000076600000240000000000612426466422020662 0ustar jrocherstaff00000000000000chaco chaco-4.5.0/MANIFEST.in0000644000076600000240000000002212426452312015106 0ustar jrocherstaff00000000000000include chaco/*.h chaco-4.5.0/PKG-INFO0000644000076600000240000000705612426466422014472 0ustar jrocherstaff00000000000000Metadata-Version: 1.1 Name: chaco Version: 4.5.0 Summary: interactive 2-dimensional plotting Home-page: http://code.enthought.com/projects/chaco Author: ETS Developers Author-email: enthought-dev@enthought.com License: BSD Download-URL: http://www.enthought.com/repo/ets/chaco-4.5.0.tar.gz Description: ========================================= chaco: interactive 2-dimensional plotting ========================================= http://github.enthought.com/chaco .. image:: https://api.travis-ci.org/enthought/chaco.png?branch=master :target: https://travis-ci.org/enthought/chaco :alt: Build status .. image:: https://coveralls.io/repos/enthought/chaco/badge.png?branch=feature%2Fcoverall_badge :target: https://coveralls.io/r/enthought/chaco?branch=feature%2Fcoverall_badge :alt: Test coverage Chaco is a Python plotting application toolkit that facilitates writing plotting applications at all levels of complexity, from simple scripts with hard-coded data to large plotting programs with complex data interrelationships and a multitude of interactive tools. While Chaco generates attractive static plots for publication and presentation, it also works well for interactive data visualization and exploration. Features -------- - **Flexible drawing and layout**: Plots consist of graphical components which can be placed inside nestable containers for layout, positioning, and event dispatch. Every component has a configurable rendering loop with distinct layers and backbuffering. Containers can draw cooperatively so that layers span across the containment hierarchy. - **Modular and extensible architecture**: Chaco is object-oriented from the ground up for ease of extension and customization. There are clear interfaces and abstract classes defining extension points for writing your own custom behaviors, from custom tools, plot types, layouts, etc. Most classes are also "subclass-friendly", so that subclasses can override one or two methods and everything else just works. - **Data model for ease of extension and embedding**: Chaco separates the data from any transformations of the data that are needed for displaying it. This separation makes it easier to extend Chaco, or embed it in applications. Prerequisites ------------- You must have the following libraries installed before building or installing Chaco: * `Numpy `_ * `distribute `_ * `enable `_ Platform: Windows Platform: Linux Platform: Mac OS-X Platform: Unix Platform: Solaris Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Science/Research Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: MacOS Classifier: Operating System :: Microsoft :: Windows Classifier: Operating System :: OS Independent Classifier: Operating System :: POSIX Classifier: Operating System :: Unix Classifier: Programming Language :: C Classifier: Programming Language :: Python Classifier: Topic :: Scientific/Engineering Classifier: Topic :: Software Development Classifier: Topic :: Software Development :: Libraries chaco-4.5.0/README.rst0000644000076600000240000000425412426452312015052 0ustar jrocherstaff00000000000000========================================= chaco: interactive 2-dimensional plotting ========================================= http://github.enthought.com/chaco .. image:: https://api.travis-ci.org/enthought/chaco.png?branch=master :target: https://travis-ci.org/enthought/chaco :alt: Build status .. image:: https://coveralls.io/repos/enthought/chaco/badge.png?branch=feature%2Fcoverall_badge :target: https://coveralls.io/r/enthought/chaco?branch=feature%2Fcoverall_badge :alt: Test coverage Chaco is a Python plotting application toolkit that facilitates writing plotting applications at all levels of complexity, from simple scripts with hard-coded data to large plotting programs with complex data interrelationships and a multitude of interactive tools. While Chaco generates attractive static plots for publication and presentation, it also works well for interactive data visualization and exploration. Features -------- - **Flexible drawing and layout**: Plots consist of graphical components which can be placed inside nestable containers for layout, positioning, and event dispatch. Every component has a configurable rendering loop with distinct layers and backbuffering. Containers can draw cooperatively so that layers span across the containment hierarchy. - **Modular and extensible architecture**: Chaco is object-oriented from the ground up for ease of extension and customization. There are clear interfaces and abstract classes defining extension points for writing your own custom behaviors, from custom tools, plot types, layouts, etc. Most classes are also "subclass-friendly", so that subclasses can override one or two methods and everything else just works. - **Data model for ease of extension and embedding**: Chaco separates the data from any transformations of the data that are needed for displaying it. This separation makes it easier to extend Chaco, or embed it in applications. Prerequisites ------------- You must have the following libraries installed before building or installing Chaco: * `Numpy `_ * `distribute `_ * `enable `_ chaco-4.5.0/setup.cfg0000644000076600000240000000007312426466422015206 0ustar jrocherstaff00000000000000[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 chaco-4.5.0/setup.py0000644000076600000240000000456712426464453015115 0ustar jrocherstaff00000000000000# Copyright (c) 2008-2014 by Enthought, Inc. # All rights reserved. from os.path import join from numpy import get_include from setuptools import setup, Extension, find_packages info = {} execfile(join('chaco', '__init__.py'), info) numpy_include_dir = get_include() # Register Python extensions contour = Extension( 'chaco.contour.contour', sources=['chaco/contour/cntr.c'], include_dirs=[numpy_include_dir], define_macros=[('NUMPY', None)] ) cython_speedups = Extension( 'chaco._cython_speedups', sources=['chaco/_cython_speedups.c'], include_dirs=[numpy_include_dir], ) # Commenting this out for now, until we get the module fully tested and working #speedups = Extension( # 'chaco._speedups', # sources = ['chaco/_speedups.cpp'], # include_dirs = [get_include()], # define_macros=[('NUMPY', None)] # ) setup( name = 'chaco', version = info['__version__'], author = 'Peter Wang, et. al.', author_email = 'pwang@enthought.com', maintainer = 'ETS Developers', maintainer_email = 'enthought-dev@enthought.com', url = 'http://code.enthought.com/projects/chaco', classifiers = [c.strip() for c in """\ Development Status :: 5 - Production/Stable Intended Audience :: Developers Intended Audience :: Science/Research License :: OSI Approved :: BSD License Operating System :: MacOS Operating System :: Microsoft :: Windows Operating System :: OS Independent Operating System :: POSIX Operating System :: Unix Programming Language :: C Programming Language :: Python Topic :: Scientific/Engineering Topic :: Software Development Topic :: Software Development :: Libraries """.splitlines() if len(c.strip()) > 0], package_data={'chaco': ['tools/toolbars/images/*.png', 'layers/data/*.svg']}, description = 'interactive 2-dimensional plotting', long_description = open('README.rst').read(), download_url = ('http://www.enthought.com/repo/ets/chaco-%s.tar.gz' % info['__version__']), ext_modules = [contour, cython_speedups], include_package_data = True, install_requires = info['__requires__'], license = 'BSD', packages = find_packages(), platforms = ["Windows", "Linux", "Mac OS-X", "Unix", "Solaris"], zip_safe = False, )