From 3281bcd91226afc32361fe6aedab7c3c3b212d10 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sat, 14 May 2016 16:57:51 -0400 Subject: [PATCH] Merge pull request #6382 from anntzer/rgba-support ENH/API: New color conversion machinery --- doc/conf.py | 16 +- doc/users/colors.rst | 2 +- doc/users/whats_new/rgba-support.rst | 11 + examples/api/collections_demo.py | 5 +- examples/color/named_colors.py | 41 +- examples/event_handling/lasso_demo.py | 7 +- examples/mplot3d/polys3d_demo.py | 4 +- examples/pylab_examples/colours.py | 4 +- examples/pylab_examples/demo_ribbon_box.py | 2 +- examples/pylab_examples/line_collection.py | 4 +- examples/widgets/menu.py | 4 +- lib/matplotlib/_color_data.py | 12 + lib/matplotlib/axes/_axes.py | 8 +- lib/matplotlib/axes/_base.py | 8 +- lib/matplotlib/backend_bases.py | 4 +- lib/matplotlib/backends/backend_gtk.py | 16 +- lib/matplotlib/backends/backend_gtk3.py | 6 +- lib/matplotlib/collections.py | 10 +- lib/matplotlib/colors.py | 437 ++++++++++----------- lib/matplotlib/finance.py | 47 +-- lib/matplotlib/image.py | 2 +- lib/matplotlib/legend_handler.py | 2 +- lib/matplotlib/lines.py | 15 +- lib/matplotlib/mathtext.py | 10 +- lib/matplotlib/patches.py | 7 +- lib/matplotlib/patheffects.py | 9 +- lib/matplotlib/tests/test_cbook.py | 2 +- lib/matplotlib/tests/test_colors.py | 41 +- lib/matplotlib/tests/test_rcparams.py | 28 +- lib/mpl_toolkits/mplot3d/art3d.py | 14 +- lib/mpl_toolkits/mplot3d/axes3d.py | 11 +- 31 files changed, 383 insertions(+), 406 deletions(-) create mode 100644 doc/users/whats_new/rgba-support.rst diff --git a/doc/conf.py b/doc/conf.py index d92366401287..54273ca5bc6e 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -333,5 +333,19 @@ def getapi(*args): sys.modules['sip'] = mocksip sys.modules['PyQt4'] = mockpyqt4 -################# numpydoc config #################### +# numpydoc config + numpydoc_show_class_members = False + +# Skip deprecated members + +def skip_deprecated(app, what, name, obj, skip, options): + if skip: + return skip + skipped = {"matplotlib.colors": ["ColorConverter", "hex2color", "rgb2hex"]} + skip_list = skipped.get(getattr(obj, "__module__", None)) + if skip_list is not None: + return getattr(obj, "__name__", None) in skip_list + +def setup(app): + app.connect('autodoc-skip-member', skip_deprecated) diff --git a/doc/users/colors.rst b/doc/users/colors.rst index f935edd5c935..4ae509601351 100644 --- a/doc/users/colors.rst +++ b/doc/users/colors.rst @@ -9,7 +9,7 @@ it can be provided as: * ``(r, g, b)`` tuples * ``(r, g, b, a)`` tuples -* hex string, ex ``#OFOFOF`` +* hex string, ex ``#0F0F0F``, or ``#0F0F0F0F`` (with alpha channel) * float value between [0, 1] for gray level * One of ``{'b', 'g', 'r', 'c', 'm', 'y', 'k', 'w'}`` * valid CSS4/X11 color names diff --git a/doc/users/whats_new/rgba-support.rst b/doc/users/whats_new/rgba-support.rst new file mode 100644 index 000000000000..9a844e3825ae --- /dev/null +++ b/doc/users/whats_new/rgba-support.rst @@ -0,0 +1,11 @@ +Improved color conversion API and RGBA support +---------------------------------------------- + +The :module:`~matplotlib.colors` gained a new color conversion API with +full support for the alpha channel. The main public functions are +:func:`~matplotlib.colors.is_color_like`, :func:`matplotlib.colors.to_rgba`, +:func:`matplotlib.colors.to_rgba_array` and :func:`~matplotlib.colors.to_hex`. +RGBA quadruplets are encoded in hex format as `#rrggbbaa`. + +A side benefit is that the Qt options editor now allows setting the alpha +channel of the artists as well. diff --git a/examples/api/collections_demo.py b/examples/api/collections_demo.py index beab1edb2981..24d126eb0489 100644 --- a/examples/api/collections_demo.py +++ b/examples/api/collections_demo.py @@ -18,8 +18,7 @@ ''' import matplotlib.pyplot as plt -from matplotlib import collections, transforms -from matplotlib.colors import colorConverter +from matplotlib import collections, colors, transforms import numpy as np nverts = 50 @@ -39,7 +38,7 @@ xyo = list(zip(xo, yo)) # Make a list of colors cycling through the default series. -colors = [colorConverter.to_rgba(c) +colors = [colors.to_rgba(c) for c in plt.rcParams['axes.prop_cycle'].by_key()['color']] fig, axes = plt.subplots(2, 2) diff --git a/examples/color/named_colors.py b/examples/color/named_colors.py index 82ae356031d0..232b514eda30 100644 --- a/examples/color/named_colors.py +++ b/examples/color/named_colors.py @@ -11,36 +11,19 @@ import numpy as np import matplotlib.pyplot as plt -from matplotlib import colors +from matplotlib import colors as mcolors -colors_ = list(six.iteritems(colors.cnames)) - -# Add the single letter colors. -for name, rgb in six.iteritems(colors.ColorConverter.colors): - hex_ = colors.rgb2hex(rgb) - colors_.append((name, hex_)) - -# Transform to hex color values. -hex_ = [color[1] for color in colors_] -# Get the rgb equivalent. -rgb = [colors.hex2color(color) for color in hex_] -# Get the hsv equivalent. -hsv = [colors.rgb_to_hsv(color) for color in rgb] - -# Split the hsv values to sort. -hue = [color[0] for color in hsv] -sat = [color[1] for color in hsv] -val = [color[2] for color in hsv] - -# Get the color names by themselves. -names = [color[0] for color in colors_] +colors = dict(mcolors.BASE_COLORS, **mcolors.CSS4_COLORS) # Sort by hue, saturation, value and name. -ind = np.lexsort((names, val, sat, hue)) -sorted_colors = [colors_[i] for i in ind] +by_hsv = sorted((tuple(mcolors.rgb_to_hsv(mcolors.to_rgba(color)[:3])), name) + for name, color in colors.items()) + +# Get the sorted color names. +sorted_names = [name for hsv, name in by_hsv] -n = len(sorted_colors) +n = len(sorted_names) ncols = 4 nrows = int(np.ceil(1. * n / ncols)) @@ -53,7 +36,7 @@ # col width w = X / ncols -for i, (name, color) in enumerate(sorted_colors): +for i, name in enumerate(sorted_names): col = i % ncols row = int(i / ncols) y = Y - (row * h) - h @@ -68,8 +51,10 @@ # Add extra black line a little bit thicker to make # clear colors more visible. - ax.hlines(y, xi_line, xf_line, color='black', linewidth=(h * 0.7)) - ax.hlines(y + h * 0.1, xi_line, xf_line, color=color, linewidth=(h * 0.6)) + ax.hlines( + y, xi_line, xf_line, color='black', linewidth=(h * 0.7)) + ax.hlines( + y + h * 0.1, xi_line, xf_line, color=colors[name], linewidth=(h * 0.6)) ax.set_xlim(0, X) ax.set_ylim(0, Y) diff --git a/examples/event_handling/lasso_demo.py b/examples/event_handling/lasso_demo.py index 75e44286c548..c704692e1277 100644 --- a/examples/event_handling/lasso_demo.py +++ b/examples/event_handling/lasso_demo.py @@ -7,9 +7,8 @@ usable as is). There will be some refinement of the API. """ from matplotlib.widgets import Lasso -from matplotlib.colors import colorConverter from matplotlib.collections import RegularPolyCollection -from matplotlib import path +from matplotlib import colors as mcolors, path import matplotlib.pyplot as plt from numpy import nonzero @@ -17,8 +16,8 @@ class Datum(object): - colorin = colorConverter.to_rgba('red') - colorout = colorConverter.to_rgba('blue') + colorin = mcolors.to_rgba("red") + colorout = mcolors.to_rgba("blue") def __init__(self, x, y, include=False): self.x = x diff --git a/examples/mplot3d/polys3d_demo.py b/examples/mplot3d/polys3d_demo.py index f9f6b97ed7e2..c44fa7338606 100644 --- a/examples/mplot3d/polys3d_demo.py +++ b/examples/mplot3d/polys3d_demo.py @@ -1,7 +1,7 @@ from mpl_toolkits.mplot3d import Axes3D from matplotlib.collections import PolyCollection -from matplotlib.colors import colorConverter import matplotlib.pyplot as plt +from matplotlib import colors as mcolors import numpy as np @@ -10,7 +10,7 @@ def cc(arg): - return colorConverter.to_rgba(arg, alpha=0.6) + return mcolors.to_rgba(arg, alpha=0.6) xs = np.arange(0, 10, 0.4) verts = [] diff --git a/examples/pylab_examples/colours.py b/examples/pylab_examples/colours.py index 4694fa4f09d9..7a9de128fe43 100644 --- a/examples/pylab_examples/colours.py +++ b/examples/pylab_examples/colours.py @@ -4,12 +4,12 @@ Some simple functions to generate colours. """ import numpy as np -from matplotlib.colors import colorConverter +from matplotlib import colors as mcolors def pastel(colour, weight=2.4): """ Convert colour into a nice pastel shade""" - rgb = np.asarray(colorConverter.to_rgb(colour)) + rgb = np.asarray(mcolors.to_rgba(colour)[:3]) # scale colour maxc = max(rgb) if maxc < 1.0 and maxc > 0: diff --git a/examples/pylab_examples/demo_ribbon_box.py b/examples/pylab_examples/demo_ribbon_box.py index df39dede071a..59eeddaa9f48 100644 --- a/examples/pylab_examples/demo_ribbon_box.py +++ b/examples/pylab_examples/demo_ribbon_box.py @@ -18,7 +18,7 @@ class RibbonBox(object): nx = original_image.shape[1] def __init__(self, color): - rgb = matplotlib.colors.colorConverter.to_rgb(color) + rgb = matplotlib.colors.to_rgba(color)[:3] im = np.empty(self.original_image.shape, self.original_image.dtype) diff --git a/examples/pylab_examples/line_collection.py b/examples/pylab_examples/line_collection.py index aefa7d0fe389..22ccbc37c0bc 100644 --- a/examples/pylab_examples/line_collection.py +++ b/examples/pylab_examples/line_collection.py @@ -1,6 +1,6 @@ import matplotlib.pyplot as plt from matplotlib.collections import LineCollection -from matplotlib.colors import colorConverter +from matplotlib import colors as mcolors import numpy as np @@ -30,7 +30,7 @@ # where onoffseq is an even length tuple of on and off ink in points. # If linestyle is omitted, 'solid' is used # See matplotlib.collections.LineCollection for more information -colors = [colorConverter.to_rgba(c) +colors = [mcolors.to_rgba(c) for c in plt.rcParams['axes.prop_cycle'].by_key()['color']] line_segments = LineCollection(segs, linewidths=(0.5, 1, 1.5, 2), diff --git a/examples/widgets/menu.py b/examples/widgets/menu.py index d6c5be4dfb98..846dd5ce1917 100644 --- a/examples/widgets/menu.py +++ b/examples/widgets/menu.py @@ -17,8 +17,8 @@ def __init__(self, fontsize=14, labelcolor='black', bgcolor='yellow', self.bgcolor = bgcolor self.alpha = alpha - self.labelcolor_rgb = colors.colorConverter.to_rgb(labelcolor) - self.bgcolor_rgb = colors.colorConverter.to_rgb(bgcolor) + self.labelcolor_rgb = colors.to_rgba(labelcolor)[:3] + self.bgcolor_rgb = colors.to_rgba(bgcolor)[:3] class MenuItem(artist.Artist): diff --git a/lib/matplotlib/_color_data.py b/lib/matplotlib/_color_data.py index f19c3e5d4af6..5d6aae709971 100644 --- a/lib/matplotlib/_color_data.py +++ b/lib/matplotlib/_color_data.py @@ -3,6 +3,18 @@ from matplotlib.externals import six + +BASE_COLORS = { + 'b': (0, 0, 1), + 'g': (0, 0.5, 0), + 'r': (1, 0, 0), + 'c': (0, 0.75, 0.75), + 'm': (0.75, 0, 0.75), + 'y': (0.75, 0.75, 0), + 'k': (0, 0, 0), + 'w': (1, 1, 1)} + + # This mapping of color names -> hex values is taken from # a survey run by Randel Monroe see: # http://blog.xkcd.com/2010/05/03/color-survey-results/ diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index abe8c43d1fe0..ce852d75cb8a 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -2065,7 +2065,7 @@ def make_iterable(x): if color is None: color = [None] * nbars else: - color = list(mcolors.colorConverter.to_rgba_array(color)) + color = list(mcolors.to_rgba_array(color)) if len(color) == 0: # until to_rgba_array is changed color = [[0, 0, 0, 0]] if len(color) < nbars: @@ -2074,7 +2074,7 @@ def make_iterable(x): if edgecolor is None: edgecolor = [None] * nbars else: - edgecolor = list(mcolors.colorConverter.to_rgba_array(edgecolor)) + edgecolor = list(mcolors.to_rgba_array(edgecolor)) if len(edgecolor) == 0: # until to_rgba_array is changed edgecolor = [[0, 0, 0, 0]] if len(edgecolor) < nbars: @@ -3843,7 +3843,7 @@ def scatter(self, x, y, s=None, c=None, marker=None, cmap=None, norm=None, co = kwargs.pop('color', None) if co is not None: try: - mcolors.colorConverter.to_rgba_array(co) + mcolors.to_rgba_array(co) except ValueError: raise ValueError("'color' kwarg must be an mpl color" " spec or sequence of color specs.\n" @@ -6050,7 +6050,7 @@ def _normalize_input(inp, ename='input'): if color is None: color = [self._get_lines.get_next_color() for i in xrange(nx)] else: - color = mcolors.colorConverter.to_rgba_array(color) + color = mcolors.to_rgba_array(color) if len(color) != nx: raise ValueError("color kwarg must have one color per dataset") diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index d56820de3840..888083928b52 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -68,7 +68,7 @@ def _process_plot_format(fmt): # Is fmt just a colorspec? try: - color = mcolors.colorConverter.to_rgb(fmt) + color = mcolors.to_rgba(fmt) # We need to differentiate grayscale '1.0' from tri_down marker '1' try: @@ -112,14 +112,14 @@ def _process_plot_format(fmt): raise ValueError( 'Illegal format string "%s"; two marker symbols' % fmt) marker = c - elif c in mcolors.colorConverter.colors: + elif c in mcolors.get_named_colors_mapping(): if color is not None: raise ValueError( 'Illegal format string "%s"; two color symbols' % fmt) color = c elif c == 'C' and i < len(chars) - 1: color_cycle_number = int(chars[i + 1]) - color = mcolors.colorConverter._get_nth_color(color_cycle_number) + color = mcolors.to_rgba("C{}".format(color_cycle_number)) i += 1 else: raise ValueError( @@ -3636,7 +3636,7 @@ def set_cursor_props(self, *args): lw, c = args else: raise ValueError('args must be a (linewidth, color) tuple') - c = mcolors.colorConverter.to_rgba(c) + c = mcolors.to_rgba(c) self._cursorProps = lw, c def get_children(self): diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index 6c72f16020ae..38a2e9b9b0e7 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -1022,11 +1022,11 @@ def set_foreground(self, fg, isRGBA=False): if self._forced_alpha and isRGBA: self._rgb = fg[:3] + (self._alpha,) elif self._forced_alpha: - self._rgb = colors.colorConverter.to_rgba(fg, self._alpha) + self._rgb = colors.to_rgba(fg, self._alpha) elif isRGBA: self._rgb = fg else: - self._rgb = colors.colorConverter.to_rgba(fg) + self._rgb = colors.to_rgba(fg) def set_graylevel(self, frac): """ diff --git a/lib/matplotlib/backends/backend_gtk.py b/lib/matplotlib/backends/backend_gtk.py index 7ebca484d7b2..de5fcce665bf 100644 --- a/lib/matplotlib/backends/backend_gtk.py +++ b/lib/matplotlib/backends/backend_gtk.py @@ -35,15 +35,11 @@ def fn_name(): return sys._getframe(1).f_code.co_name from matplotlib.backends.backend_gdk import RendererGDK, FigureCanvasGDK from matplotlib.cbook import is_string_like, is_writable_file_like -from matplotlib.colors import colorConverter from matplotlib.figure import Figure from matplotlib.widgets import SubplotTool -from matplotlib import lines -from matplotlib import markers -from matplotlib import cbook -from matplotlib import verbose -from matplotlib import rcParams +from matplotlib import ( + cbook, colors as mcolors, lines, markers, rcParams, verbose) backend_version = "%d.%d.%d" % gtk.pygtk_version @@ -1003,13 +999,13 @@ def on_combobox_lineprops_changed(self, item): if marker is None: marker = 'None' self.cbox_markers.set_active(self.markerd[marker]) - r,g,b = colorConverter.to_rgb(line.get_color()) - color = gtk.gdk.Color(*[int(val*65535) for val in (r,g,b)]) + rgba = mcolors.to_rgba(line.get_color()) + color = gtk.gdk.Color(*[int(val*65535) for val in rgba[:3]]) button = self.wtree.get_widget('colorbutton_linestyle') button.set_color(color) - r,g,b = colorConverter.to_rgb(line.get_markerfacecolor()) - color = gtk.gdk.Color(*[int(val*65535) for val in (r,g,b)]) + rgba = mcolors.to_rgba(line.get_markerfacecolor()) + color = gtk.gdk.Color(*[int(val*65535) for val in rgba[:3]]) button = self.wtree.get_widget('colorbutton_markerface') button.set_color(color) self._updateson = True diff --git a/lib/matplotlib/backends/backend_gtk3.py b/lib/matplotlib/backends/backend_gtk3.py index d8f2910c7f35..4f2ef04c5873 100644 --- a/lib/matplotlib/backends/backend_gtk3.py +++ b/lib/matplotlib/backends/backend_gtk3.py @@ -36,14 +36,10 @@ def fn_name(): return sys._getframe(1).f_code.co_name from matplotlib import backend_tools from matplotlib.cbook import is_string_like, is_writable_file_like -from matplotlib.colors import colorConverter from matplotlib.figure import Figure from matplotlib.widgets import SubplotTool -from matplotlib import lines -from matplotlib import cbook -from matplotlib import verbose -from matplotlib import rcParams +from matplotlib import cbook, colors as mcolors, lines, verbose, rcParams backend_version = "%s.%s.%s" % (Gtk.get_major_version(), Gtk.get_micro_version(), Gtk.get_minor_version()) diff --git a/lib/matplotlib/collections.py b/lib/matplotlib/collections.py index 5576def9d440..60fed3e41403 100644 --- a/lib/matplotlib/collections.py +++ b/lib/matplotlib/collections.py @@ -615,7 +615,7 @@ def set_facecolor(self, c): if c is None: c = mpl.rcParams['patch.facecolor'] self._facecolors_original = c - self._facecolors = mcolors.colorConverter.to_rgba_array(c, self._alpha) + self._facecolors = mcolors.to_rgba_array(c, self._alpha) self.stale = True def set_facecolors(self, c): @@ -663,7 +663,7 @@ def set_edgecolor(self, c): if c is None: c = mpl.rcParams['patch.edgecolor'] self._edgecolors_original = c - self._edgecolors = mcolors.colorConverter.to_rgba_array(c, self._alpha) + self._edgecolors = mcolors.to_rgba_array(c, self._alpha) self.stale = True def set_edgecolors(self, c): @@ -684,14 +684,14 @@ def set_alpha(self, alpha): raise TypeError('alpha must be a float or None') artist.Artist.set_alpha(self, alpha) try: - self._facecolors = mcolors.colorConverter.to_rgba_array( + self._facecolors = mcolors.to_rgba_array( self._facecolors_original, self._alpha) except (AttributeError, TypeError, IndexError): pass try: if (not isinstance(self._edgecolors_original, six.string_types) or self._edgecolors_original != str('face')): - self._edgecolors = mcolors.colorConverter.to_rgba_array( + self._edgecolors = mcolors.to_rgba_array( self._edgecolors_original, self._alpha) except (AttributeError, TypeError, IndexError): pass @@ -1137,7 +1137,7 @@ def __init__(self, segments, # Can be None. if antialiaseds is None: antialiaseds = (mpl.rcParams['lines.antialiased'],) - colors = mcolors.colorConverter.to_rgba_array(colors) + colors = mcolors.to_rgba_array(colors) Collection.__init__( self, diff --git a/lib/matplotlib/colors.py b/lib/matplotlib/colors.py index b40ad57575cc..8a2efaea3f50 100644 --- a/lib/matplotlib/colors.py +++ b/lib/matplotlib/colors.py @@ -15,21 +15,23 @@ :class:`ListedColormap`, which is used for generating a custom colormap from a list of color specifications. -The module also provides a single instance, *colorConverter*, of the -:class:`ColorConverter` class providing methods for converting single color -specifications or sequences of them to *RGB* or *RGBA*. +The module also provides functions for checking whether an object can be +interpreted as a color (:func:`is_color_like`), for converting such an object +to an RGBA tuple (:func:`to_rgba`) or to an HTML-like hex string in the +`#rrggbb` format (:func:`to_hex`), and a sequence of colors to an `(n, 4)` +RGBA array (:func:`to_rgba_array`). Caching is used for efficiency. Commands which take color arguments can use several formats to specify the colors. For the basic built-in colors, you can use a single letter - - b: blue - - g: green - - r: red - - c: cyan - - m: magenta - - y: yellow - - k: black - - w: white + - `b`: blue + - `g`: green + - `r`: red + - `c`: cyan + - `m`: magenta + - `y`: yellow + - `k`: black + - `w`: white To use the colors that are part of the active color cycle in the current style, use `C` followed by a digit. For example: @@ -44,14 +46,16 @@ For a greater range of colors, you have two options. You can specify the color using an html hex string, as in:: - color = '#eeefff' + color = '#eeefff' -or you can pass an *R* , *G* , *B* tuple, where each of *R* , *G* , *B* are in -the range [0,1]. +(possibly specifying an alpha value as well), or you can pass an `(r, g, b)` +or `(r, g, b, a)` tuple, where each of `r`, `g`, `b` and `a` are in the range +[0,1]. Finally, legal html names for colors, like 'red', 'burlywood' and 'chartreuse' are supported. """ + from __future__ import (absolute_import, division, print_function, unicode_literals) import re @@ -62,55 +66,211 @@ import numpy as np from numpy import ma import matplotlib.cbook as cbook -from ._color_data import XKCD_COLORS, CSS4_COLORS +from ._color_data import BASE_COLORS, CSS4_COLORS, XKCD_COLORS -# for back copatibility -cnames = CSS4_COLORS -COLOR_NAMES = {'xkcd': XKCD_COLORS, - 'css4': CSS4_COLORS} +class _ColorMapping(dict): + def __init__(self, mapping): + super(_ColorMapping, self).__init__(mapping) + self.cache = {} + def __setitem__(self, key, value): + super(_ColorMapping, self).__setitem__(key, value) + self.cache.clear() -def is_color_like(c): - 'Return *True* if *c* can be converted to *RGB*' - - # Special-case the N-th color cycle syntax, because its parsing - # needs to be deferred. We may be reading a value from rcParams - # here before the color_cycle rcParam has been parsed. - if isinstance(c, bytes): - match = re.match(b'^C[0-9]$', c) - if match is not None: - return True - elif isinstance(c, six.text_type): - match = re.match('^C[0-9]$', c) - if match is not None: - return True + def __delitem__(self, key, value): + super(_ColorMapping, self).__delitem__(key, value) + self.cache.clear() - try: - colorConverter.to_rgb(c) + +_colors_full_map = {} +# Set by reverse priority order. +_colors_full_map.update(XKCD_COLORS) +_colors_full_map.update(CSS4_COLORS) +_colors_full_map.update(BASE_COLORS) +_colors_full_map = _ColorMapping(_colors_full_map) + + +def get_named_colors_mapping(): + """Return the global mapping of names to named colors. + """ + return _colors_full_map + + +def _is_nth_color(c): + """Return whether `c` can be interpreted as an item in the color cycle. + """ + return isinstance(c, six.string_types) and re.match(r"\AC[0-9]\Z", c) + + +def is_color_like(c): + """Return whether `c` can be interpreted as an RGB(A) color. + """ + # Special-case nth color syntax because it cannot be parsed during + # setup. + if _is_nth_color(c): return True + try: + to_rgba(c) except ValueError: return False + else: + return True -def rgb2hex(rgb): - 'Given an rgb or rgba sequence of 0-1 floats, return the hex string' - a = '#%02x%02x%02x' % tuple([int(np.round(val * 255)) for val in rgb[:3]]) - return a +def to_rgba(c, alpha=None): + """Convert `c` to an RGBA color. + + If `alpha` is not `None`, it forces the alpha value. + """ + # Special-case nth color syntax because it should not be cached. + if _is_nth_color(c): + from matplotlib import rcParams + from matplotlib.rcsetup import cycler + prop_cycler = rcParams['axes.prop_cycle'] + if prop_cycler is None and 'axes.color_cycle' in rcParams: + clist = rcParams['axes.color_cycle'] + prop_cycler = cycler('color', clist) + colors = prop_cycler._transpose().get('color', 'k') + c = colors[int(c[1]) % len(colors)] + try: + rgba = _colors_full_map.cache[c, alpha] + except (KeyError, TypeError): # Not in cache, or unhashable. + rgba = _to_rgba_no_colorcycle(c, alpha) + try: + _colors_full_map.cache[c, alpha] = rgba + except TypeError: + pass + return rgba + + +def _to_rgba_no_colorcycle(c, alpha=None): + """Convert `c` to an RGBA color, with no support for color-cycle syntax. + + If `alpha` is not `None`, it forces the alpha value. + """ + orig_c = c + if isinstance(c, six.string_types) and c.lower() == "none": + return (0., 0., 0., 0.) + if isinstance(c, six.string_types): + # Named color. + try: + c = _colors_full_map[c.lower()] + except KeyError: + pass + if isinstance(c, six.string_types): + # hex color with no alpha. + match = re.match(r"\A#[a-fA-F0-9]{6}\Z", c) + if match: + return (tuple(int(n, 16) / 255 + for n in [c[1:3], c[3:5], c[5:7]]) + + (alpha if alpha is not None else 1.,)) + if isinstance(c, six.string_types): + # hex color with alpha. + match = re.match(r"\A#[a-fA-F0-9]{8}\Z", c) + if match: + color = [int(n, 16) / 255 + for n in [c[1:3], c[3:5], c[5:7], c[7:9]]] + if alpha is not None: + color[-1] = alpha + return tuple(color) + if isinstance(c, six.string_types): + # string gray. + try: + return (float(c),) * 3 + (1.,) + except ValueError: + pass + raise ValueError("Invalid RGBA argument: {!r}".format(orig_c)) + # tuple color. + # Python 2.7 / numpy 1.6 apparently require this to return builtin floats, + # not numpy floats. + try: + c = tuple(map(float, c)) + except TypeError: + raise ValueError("Invalid RGBA argument: {!r}".format(orig_c)) + if len(c) not in [3, 4]: + raise ValueError("RGBA sequence should have length 3 or 4") + if len(c) == 3 and alpha is None: + alpha = 1 + if alpha is not None: + c = c[:3] + (alpha,) + if any(elem < 0 or elem > 1 for elem in c): + raise ValueError("RGBA values should be within 0-1 range") + return c + + +def to_rgba_array(c, alpha=None): + """Convert `c` to a (n, 4) array of RGBA colors. + + If `alpha` is not `None`, it forces the alpha value. If `c` is "none" + (case-insensitive) or an empty list, an empty array is returned. + """ + # Single value? + if isinstance(c, six.string_types) and c.lower() == "none": + return np.zeros((0, 4), float) + try: + return np.array([to_rgba(c, alpha)], float) + except (ValueError, TypeError): + pass + # Special-case inputs that are already arrays, for performance. (If the + # array has the wrong kind or shape, raise the error during one-at-a-time + # conversion.) + if (isinstance(c, np.ndarray) and c.dtype.kind in "if" + and c.ndim == 2 and c.shape[1] in [3, 4]): + if c.shape[1] == 3: + result = np.column_stack([c, np.zeros(len(c))]) + result[:, -1] = alpha if alpha is not None else 1. + elif c.shape[1] == 4: + result = c.copy() + if alpha is not None: + result[:, -1] = alpha + if np.any((result < 0) | (result > 1)): + raise ValueError("RGBA values should be within 0-1 range") + return result + # Convert one at a time. + result = np.empty((len(c), 4), float) + for i, cc in enumerate(c): + result[i] = to_rgba(cc, alpha) + return result + +def to_rgb(c): + """Convert `c` to an RGB color, silently dropping the alpha channel. + """ + return to_rgba(c)[:3] + + +def to_hex(c, keep_alpha=False): + """Convert `c` to a hex color. + + Uses the #rrggbb format if `keep_alpha` is False (the default), `#rrggbbaa` + otherwise. + """ + c = to_rgba(c) + if not keep_alpha: + c = c[:3] + return "#" + "".join(format(int(np.round(val * 255)), "02x") + for val in c) + + +### Backwards-compatible color-conversion API + +cnames = CSS4_COLORS +COLOR_NAMES = {'xkcd': XKCD_COLORS, 'css4': CSS4_COLORS} hexColorPattern = re.compile("\A#[a-fA-F0-9]{6}\Z") -def hex2color(s): +def rgb2hex(c): + 'Given an rgb or rgba sequence of 0-1 floats, return the hex string' + return to_hex(c) + + +def hex2color(c): """ Take a hex string *s* and return the corresponding rgb 3-tuple Example: #efefef -> (0.93725, 0.93725, 0.93725) """ - if not isinstance(s, six.string_types): - raise TypeError('hex2color requires a string argument') - if hexColorPattern.match(s) is None: - raise ValueError('invalid hex color string "%s"' % s) - return tuple([int(n, 16) / 255.0 for n in (s[1:3], s[3:5], s[5:7])]) + return ColorConverter.to_rgb(c) class ColorConverter(object): @@ -123,47 +283,12 @@ class ColorConverter(object): Ordinarily only the single instance instantiated in this module, *colorConverter*, is needed. """ - colors = { - 'b': (0, 0, 1), - 'g': (0, 0.5, 0), - 'r': (1, 0, 0), - 'c': (0, 0.75, 0.75), - 'm': (0.75, 0, 0.75), - 'y': (0.75, 0.75, 0), - 'k': (0, 0, 0), - 'w': (1, 1, 1)} - - _prop_cycler = None - - cache = {} - CN_LOOKUPS = [COLOR_NAMES[k] for k in ['css4', 'xkcd']] - - @classmethod - def _get_nth_color(cls, val): - """ - Get the Nth color in the current color cycle. If N is greater - than the number of colors in the cycle, it is wrapped around. - """ - from matplotlib.rcsetup import cycler - from matplotlib import rcParams - - prop_cycler = rcParams['axes.prop_cycle'] - if prop_cycler is None and 'axes.color_cycle' in rcParams: - clist = rcParams['axes.color_cycle'] - prop_cycler = cycler('color', clist) - - colors = prop_cycler._transpose()['color'] - return colors[val % len(colors)] - - @classmethod - def _parse_nth_color(cls, val): - match = re.match('^C[0-9]$', val) - if match is not None: - return cls._get_nth_color(int(val[1])) - raise ValueError("Not a color cycle color") + colors = _colors_full_map + cache = _colors_full_map.cache - def to_rgb(self, arg): + @staticmethod + def to_rgb(arg): """ Returns an *RGB* tuple of three floats from 0-1. @@ -178,70 +303,10 @@ def to_rgb(self, arg): if *arg* is *RGBA*, the *A* will simply be discarded. """ - # Gray must be a string to distinguish 3-4 grays from RGB or RGBA. + return to_rgb(arg) - try: - return self.cache[arg] - except KeyError: - pass - except TypeError: # could be unhashable rgb seq - arg = tuple(arg) - try: - return self.cache[arg] - except KeyError: - pass - except TypeError: - raise ValueError( - 'to_rgb: arg "%s" is unhashable even inside a tuple' - % (str(arg),)) - try: - if cbook.is_string_like(arg): - argl = arg.lower() - color = self.colors.get(argl, None) - if color is None: - try: - argl = self._parse_nth_color(arg) - # in this case we do not want to cache in case - # the rcparam changes, recurse with the actual color - # value - return self.to_rgb(argl) - except ValueError: - pass - for cmapping in self.CN_LOOKUPS: - str1 = cmapping.get(argl, argl) - if str1 != argl: - break - if str1.startswith('#'): - color = hex2color(str1) - else: - fl = float(argl) - if fl < 0 or fl > 1: - raise ValueError( - 'gray (string) must be in range 0-1') - color = (fl,) * 3 - elif cbook.iterable(arg): - if len(arg) > 4 or len(arg) < 3: - raise ValueError( - 'sequence length is %d; must be 3 or 4' % len(arg)) - color = tuple(arg[:3]) - if [x for x in color if (float(x) < 0) or (x > 1)]: - # This will raise TypeError if x is not a number. - raise ValueError( - 'number in rbg sequence outside 0-1 range') - else: - raise ValueError( - 'cannot convert argument to rgb sequence') - self.cache[arg] = color - - except (KeyError, ValueError, TypeError) as exc: - raise ValueError( - 'to_rgb: Invalid rgb arg "%s"\n%s' % (str(arg), exc)) - # Error messages could be improved by handling TypeError - # separately; but this should be rare and not too hard - # for the user to figure out as-is. - return color - - def to_rgba(self, arg, alpha=None): + @staticmethod + def to_rgba(arg, alpha=None): """ Returns an *RGBA* tuple of four floats from 0-1. @@ -251,42 +316,10 @@ def to_rgba(self, arg, alpha=None): If *arg* is an *RGBA* sequence and *alpha* is not *None*, *alpha* will replace the original *A*. """ - try: - if arg.lower() == 'none': - return (0.0, 0.0, 0.0, 0.0) - except AttributeError: - pass - - if alpha is not None and (alpha < 0.0 or alpha > 1.0): - raise ValueError("alpha must be in range 0-1") + return to_rgba(arg, alpha) - try: - if not cbook.is_string_like(arg) and cbook.iterable(arg): - if len(arg) == 4: - if any(float(x) < 0 or x > 1 for x in arg): - raise ValueError( - 'number in rbga sequence outside 0-1 range') - if alpha is None: - return tuple(arg) - return arg[0], arg[1], arg[2], alpha - if len(arg) == 3: - r, g, b = arg - if any(float(x) < 0 or x > 1 for x in arg): - raise ValueError( - 'number in rbg sequence outside 0-1 range') - else: - raise ValueError( - 'length of rgba sequence should be either 3 or 4') - else: - r, g, b = self.to_rgb(arg) - if alpha is None: - alpha = 1.0 - return r, g, b, alpha - except (TypeError, ValueError) as exc: - raise ValueError( - 'to_rgba: Invalid rgba arg "%s"\n%s' % (str(arg), exc)) - - def to_rgba_array(self, c, alpha=None): + @staticmethod + def to_rgba_array(arg, alpha=None): """ Returns a numpy array of *RGBA* tuples. @@ -295,47 +328,13 @@ def to_rgba_array(self, c, alpha=None): Special case to handle "no color": if *c* is "none" (case-insensitive), then an empty array will be returned. Same for an empty list. """ - try: - nc = len(c) - except TypeError: - raise ValueError( - "Cannot convert argument type %s to rgba array" % type(c)) - try: - if nc == 0 or c.lower() == 'none': - return np.zeros((0, 4), dtype=np.float) - except AttributeError: - pass - try: - # Single value? Put it in an array with a single row. - return np.array([self.to_rgba(c, alpha)], dtype=np.float) - except ValueError: - if isinstance(c, np.ndarray): - if c.ndim != 2 and c.dtype.kind not in 'SU': - raise ValueError("Color array must be two-dimensional") - if (c.ndim == 2 and c.shape[1] == 4 and c.dtype.kind == 'f'): - if (c.ravel() > 1).any() or (c.ravel() < 0).any(): - raise ValueError( - "number in rgba sequence is outside 0-1 range") - result = np.asarray(c, np.float) - if alpha is not None: - if alpha > 1 or alpha < 0: - raise ValueError("alpha must be in 0-1 range") - result[:, 3] = alpha - return result - # This alpha operation above is new, and depends - # on higher levels to refrain from setting alpha - # to values other than None unless there is - # intent to override any existing alpha values. - - # It must be some other sequence of color specs. - result = np.zeros((nc, 4), dtype=np.float) - for i, cc in enumerate(c): - result[i] = self.to_rgba(cc, alpha) - return result + return to_rgba_array(arg, alpha) colorConverter = ColorConverter() +### End of backwards-compatible color-conversion API + def makeMappingArray(N, data, gamma=1.0): """Create an *N* -element 1-d lookup table diff --git a/lib/matplotlib/finance.py b/lib/matplotlib/finance.py index 6b5b1b3e510d..cbdb592c8b3e 100644 --- a/lib/matplotlib/finance.py +++ b/lib/matplotlib/finance.py @@ -21,11 +21,10 @@ import numpy as np -from matplotlib import verbose, get_cachedir +from matplotlib import colors as mcolors, verbose, get_cachedir from matplotlib.dates import date2num from matplotlib.cbook import iterable, mkdirs from matplotlib.collections import LineCollection, PolyCollection -from matplotlib.colors import colorConverter from matplotlib.lines import Line2D, TICKLEFT, TICKRIGHT from matplotlib.patches import Rectangle from matplotlib.transforms import Affine2D @@ -969,13 +968,9 @@ def plot_day_summary2_ohlc(ax, opens, highs, lows, closes, ticksize=4, tickTransform = Affine2D().scale(scale, 0.0) - r, g, b = colorConverter.to_rgb(colorup) - colorup = r, g, b, 1 - r, g, b = colorConverter.to_rgb(colordown) - colordown = r, g, b, 1 - colord = {True: colorup, - False: colordown, - } + colorup = mcolors.to_rgba(colorup) + colordown = mcolors.to_rgba(colordown) + colord = {True: colorup, False: colordown} colors = [colord[open < close] for open, close in zip(opens, closes) if open != -1 and close != -1] @@ -1113,13 +1108,9 @@ def candlestick2_ohlc(ax, opens, highs, lows, closes, width=4, for i, low, high in zip(xrange(len(lows)), lows, highs) if low != -1] - r, g, b = colorConverter.to_rgb(colorup) - colorup = r, g, b, alpha - r, g, b = colorConverter.to_rgb(colordown) - colordown = r, g, b, alpha - colord = {True: colorup, - False: colordown, - } + colorup = mcolors.to_rgba(colorup, alpha) + colordown = mcolors.to_rgba(colordown, alpha) + colord = {True: colorup, False: colordown} colors = [colord[open < close] for open, close in zip(opens, closes) if open != -1 and close != -1] @@ -1186,13 +1177,9 @@ def volume_overlay(ax, opens, closes, volumes, """ - r, g, b = colorConverter.to_rgb(colorup) - colorup = r, g, b, alpha - r, g, b = colorConverter.to_rgb(colordown) - colordown = r, g, b, alpha - colord = {True: colorup, - False: colordown, - } + colorup = mcolors.to_rgba(colorup, alpha) + colordown = mcolors.to_rgba(colordown, alpha) + colord = {True: colorup, False: colordown} colors = [colord[open < close] for open, close in zip(opens, closes) if open != -1 and close != -1] @@ -1288,13 +1275,9 @@ def volume_overlay3(ax, quotes, """ - r, g, b = colorConverter.to_rgb(colorup) - colorup = r, g, b, alpha - r, g, b = colorConverter.to_rgb(colordown) - colordown = r, g, b, alpha - colord = {True: colorup, - False: colordown, - } + colorup = mcolors.to_rgba(colorup, alpha) + colordown = mcolors.to_rgba(colordown, alpha) + colord = {True: colorup, False: colordown} dates, opens, highs, lows, closes, volumes = list(zip(*quotes)) colors = [colord[close1 >= close0] @@ -1369,8 +1352,8 @@ def index_bar(ax, vals, """ - facecolors = (colorConverter.to_rgba(facecolor, alpha),) - edgecolors = (colorConverter.to_rgba(edgecolor, alpha),) + facecolors = (mcolors.to_rgba(facecolor, alpha),) + edgecolors = (mcolors.to_rgba(edgecolor, alpha),) right = width / 2.0 left = -width / 2.0 diff --git a/lib/matplotlib/image.py b/lib/matplotlib/image.py index bc6fbf807aa3..0d86e2b74081 100644 --- a/lib/matplotlib/image.py +++ b/lib/matplotlib/image.py @@ -908,7 +908,7 @@ def make_image(self, renderer, magnification=1.0, unsampled=False): if unsampled: raise ValueError('unsampled not supported on PColorImage') fc = self.axes.patch.get_facecolor() - bg = mcolors.colorConverter.to_rgba(fc, 0) + bg = mcolors.to_rgba(fc, 0) bg = (np.array(bg)*255).astype(np.uint8) l, b, r, t = self.axes.bbox.extents width = (np.round(r) + 0.5) - (np.round(l) - 0.5) diff --git a/lib/matplotlib/legend_handler.py b/lib/matplotlib/legend_handler.py index 82fbea1f88cd..5b589b0c501d 100644 --- a/lib/matplotlib/legend_handler.py +++ b/lib/matplotlib/legend_handler.py @@ -593,7 +593,7 @@ class HandlerPolyCollection(HandlerBase): """ def _update_prop(self, legend_handle, orig_handle): def first_color(colors): - colors = mcolors.colorConverter.to_rgba_array(colors) + colors = mcolors.to_rgba_array(colors) if len(colors): return colors[0] else: diff --git a/lib/matplotlib/lines.py b/lib/matplotlib/lines.py index 65fd1e90eb24..006f190925a5 100644 --- a/lib/matplotlib/lines.py +++ b/lib/matplotlib/lines.py @@ -14,12 +14,11 @@ import numpy as np from numpy import ma from matplotlib import verbose -from . import artist +from . import artist, colors as mcolors from .artist import Artist from .cbook import (iterable, is_string_like, is_numlike, ls_mapper_r, pts_to_prestep, pts_to_poststep, pts_to_midstep) -from .colors import colorConverter from .path import Path from .transforms import Bbox, TransformedPath, IdentityTransform @@ -1299,24 +1298,16 @@ def update_from(self, other): other._marker.get_fillstyle()) self._drawstyle = other._drawstyle - def _get_rgb_face(self, alt=False): - facecolor = self._get_markerfacecolor(alt=alt) - if is_string_like(facecolor) and facecolor.lower() == 'none': - rgbFace = None - else: - rgbFace = colorConverter.to_rgb(facecolor) - return rgbFace - def _get_rgba_face(self, alt=False): facecolor = self._get_markerfacecolor(alt=alt) if is_string_like(facecolor) and facecolor.lower() == 'none': rgbaFace = None else: - rgbaFace = colorConverter.to_rgba(facecolor, self._alpha) + rgbaFace = mcolors.to_rgba(facecolor, self._alpha) return rgbaFace def _get_rgba_ln_color(self, alt=False): - return colorConverter.to_rgba(self._color, self._alpha) + return mcolors.to_rgba(self._color, self._alpha) # some aliases.... def set_aa(self, val): diff --git a/lib/matplotlib/mathtext.py b/lib/matplotlib/mathtext.py index f8a06acc97e3..478d1cb379d0 100644 --- a/lib/matplotlib/mathtext.py +++ b/lib/matplotlib/mathtext.py @@ -3332,12 +3332,12 @@ def to_rgba(self, texstr, color='black', dpi=120, fontsize=14): """ x, depth = self.to_mask(texstr, dpi=dpi, fontsize=fontsize) - r, g, b = mcolors.colorConverter.to_rgb(color) + r, g, b, a = mcolors.to_rgba(color) RGBA = np.zeros((x.shape[0], x.shape[1], 4), dtype=np.uint8) - RGBA[:,:,0] = int(255*r) - RGBA[:,:,1] = int(255*g) - RGBA[:,:,2] = int(255*b) - RGBA[:,:,3] = x + RGBA[:, :, 0] = 255 * r + RGBA[:, :, 1] = 255 * g + RGBA[:, :, 2] = 255 * b + RGBA[:, :, 3] = x return RGBA, depth def to_png(self, filename, texstr, color='black', dpi=120, fontsize=14): diff --git a/lib/matplotlib/patches.py b/lib/matplotlib/patches.py index 7aebc7c5043a..fa22b681976a 100644 --- a/lib/matplotlib/patches.py +++ b/lib/matplotlib/patches.py @@ -278,7 +278,7 @@ def set_edgecolor(self, color): if color is None: color = mpl.rcParams['patch.edgecolor'] self._original_edgecolor = color - self._edgecolor = colors.colorConverter.to_rgba(color, self._alpha) + self._edgecolor = colors.to_rgba(color, self._alpha) self.stale = True def set_ec(self, color): @@ -295,7 +295,7 @@ def set_facecolor(self, color): color = mpl.rcParams['patch.facecolor'] # save: otherwise changing _fill may lose alpha information self._original_facecolor = color - self._facecolor = colors.colorConverter.to_rgba(color, self._alpha) + self._facecolor = colors.to_rgba(color, self._alpha) if not self._fill: self._facecolor = list(self._facecolor) self._facecolor[3] = 0 @@ -585,8 +585,7 @@ def _update(self): if self.props is not None: self.update(self.props) else: - r, g, b, a = colors.colorConverter.to_rgba( - self.patch.get_facecolor()) + r, g, b, a = colors.to_rgba(self.patch.get_facecolor()) rho = 0.3 r = rho * r g = rho * g diff --git a/lib/matplotlib/patheffects.py b/lib/matplotlib/patheffects.py index 5435bcc8bd00..a0c8933b9b86 100644 --- a/lib/matplotlib/patheffects.py +++ b/lib/matplotlib/patheffects.py @@ -10,9 +10,8 @@ from matplotlib.externals import six from matplotlib.backend_bases import RendererBase -import matplotlib.transforms as mtransforms -from matplotlib.colors import colorConverter -import matplotlib.patches as mpatches +from matplotlib import ( + colors as mcolors, patches as mpatches, transforms as mtransforms) class AbstractPathEffect(object): @@ -241,7 +240,7 @@ def __init__(self, offset=(2, -2), if shadow_rgbFace is None: self._shadow_rgbFace = shadow_rgbFace else: - self._shadow_rgbFace = colorConverter.to_rgba(shadow_rgbFace) + self._shadow_rgbFace = mcolors.to_rgba(shadow_rgbFace) if alpha is None: alpha = 0.3 @@ -322,7 +321,7 @@ def __init__(self, offset=(2,-2), if shadow_color is None: self._shadow_color = shadow_color else: - self._shadow_color = colorConverter.to_rgba(shadow_color) + self._shadow_color = mcolors.to_rgba(shadow_color) self._alpha = alpha self._rho = rho diff --git a/lib/matplotlib/tests/test_cbook.py b/lib/matplotlib/tests/test_cbook.py index 26d96e6dc97c..fe0377a72eba 100644 --- a/lib/matplotlib/tests/test_cbook.py +++ b/lib/matplotlib/tests/test_cbook.py @@ -86,7 +86,7 @@ def setUp(self): datetime(2008, 1, 5), datetime(2008, 1, 6)] self.arr_dt2 = np.array(self.arr_dt) self.arr_colors = ['r', 'g', 'b', 'c', 'm', 'y'] - self.arr_rgba = mcolors.colorConverter.to_rgba_array(self.arr_colors) + self.arr_rgba = mcolors.to_rgba_array(self.arr_colors) @raises(ValueError) def test_bad_first_arg(self): diff --git a/lib/matplotlib/tests/test_colors.py b/lib/matplotlib/tests/test_colors.py index 7b59ca3d865c..f8e050cb2f67 100644 --- a/lib/matplotlib/tests/test_colors.py +++ b/lib/matplotlib/tests/test_colors.py @@ -259,7 +259,7 @@ def test_cmap_and_norm_from_levels_and_colors(): def test_cmap_and_norm_from_levels_and_colors2(): levels = [-1, 2, 2.5, 3] colors = ['red', (0, 1, 0), 'blue', (0.5, 0.5, 0.5), (0.0, 0.0, 0.0, 1.0)] - clr = mcolors.colorConverter.to_rgba_array(colors) + clr = mcolors.to_rgba_array(colors) bad = (0.1, 0.1, 0.1, 0.1) no_color = (0.0, 0.0, 0.0, 0.0) masked_value = 'masked_value' @@ -336,13 +336,9 @@ def test_autoscale_masked(): def test_colors_no_float(): # Gray must be a string to distinguish 3-4 grays from RGB or RGBA. - def gray_from_float_rgb(): - return mcolors.colorConverter.to_rgb(0.4) - def gray_from_float_rgba(): - return mcolors.colorConverter.to_rgba(0.4) + return mcolors.to_rgba(0.4) - assert_raises(ValueError, gray_from_float_rgb) assert_raises(ValueError, gray_from_float_rgba) @@ -558,12 +554,8 @@ def angled_plane(azimuth, elevation, angle, x, y): def test_xkcd(): - x11_blue = mcolors.rgb2hex( - mcolors.colorConverter.to_rgb('blue')) - assert x11_blue == '#0000ff' - XKCD_blue = mcolors.rgb2hex( - mcolors.colorConverter.to_rgb('xkcd:blue')) - assert XKCD_blue == '#0343df' + assert mcolors.to_hex("blue") == "#0000ff" + assert mcolors.to_hex("xkcd:blue") == "#0343df" def _sph2cart(theta, phi): @@ -602,17 +594,26 @@ def test_pandas_iterable(): def test_cn(): matplotlib.rcParams['axes.prop_cycle'] = cycler('color', ['blue', 'r']) - x11_blue = mcolors.rgb2hex(mcolors.colorConverter.to_rgb('C0')) - assert x11_blue == '#0000ff' - red = mcolors.rgb2hex(mcolors.colorConverter.to_rgb('C1')) - assert red == '#ff0000' + assert mcolors.to_hex("C0") == '#0000ff' + assert mcolors.to_hex("C1") == '#ff0000' matplotlib.rcParams['axes.prop_cycle'] = cycler('color', ['xkcd:blue', 'r']) - XKCD_blue = mcolors.rgb2hex(mcolors.colorConverter.to_rgb('C0')) - assert XKCD_blue == '#0343df' - red = mcolors.rgb2hex(mcolors.colorConverter.to_rgb('C1')) - assert red == '#ff0000' + assert mcolors.to_hex("C0") == '#0343df' + assert mcolors.to_hex("C1") == '#ff0000' + + +def test_conversions(): + # to_rgba_array("none") returns a (0, 4) array. + assert_array_equal(mcolors.to_rgba_array("none"), np.zeros((0, 4))) + # alpha is properly set. + assert_equal(mcolors.to_rgba((1, 1, 1), .5), (1, 1, 1, .5)) + # builtin round differs between py2 and py3. + assert_equal(mcolors.to_hex((.7, .7, .7)), "#b2b2b2") + # hex roundtrip. + hex_color = "#1234abcd" + assert_equal(mcolors.to_hex(mcolors.to_rgba(hex_color), keep_alpha=True), + hex_color) if __name__ == '__main__': diff --git a/lib/matplotlib/tests/test_rcparams.py b/lib/matplotlib/tests/test_rcparams.py index d37adfc2b00d..6b759aa9a509 100644 --- a/lib/matplotlib/tests/test_rcparams.py +++ b/lib/matplotlib/tests/test_rcparams.py @@ -202,15 +202,11 @@ def test_legend_facecolor(): get_func = 'get_facecolor' rcparam = 'legend.facecolor' test_values = [({rcparam: 'r'}, - mcolors.colorConverter.to_rgba('r')), - ({rcparam: 'inherit', - 'axes.facecolor': 'r' - }, - mcolors.colorConverter.to_rgba('r')), - ({rcparam: 'g', - 'axes.facecolor': 'r'}, - mcolors.colorConverter.to_rgba('g')) - ] + mcolors.to_rgba('r')), + ({rcparam: 'inherit', 'axes.facecolor': 'r'}, + mcolors.to_rgba('r')), + ({rcparam: 'g', 'axes.facecolor': 'r'}, + mcolors.to_rgba('g'))] for rc_dict, target in test_values: yield _legend_rcparam_helper, rc_dict, target, get_func @@ -219,15 +215,11 @@ def test_legend_edgecolor(): get_func = 'get_edgecolor' rcparam = 'legend.edgecolor' test_values = [({rcparam: 'r'}, - mcolors.colorConverter.to_rgba('r')), - ({rcparam: 'inherit', - 'axes.edgecolor': 'r' - }, - mcolors.colorConverter.to_rgba('r')), - ({rcparam: 'g', - 'axes.facecolor': 'r'}, - mcolors.colorConverter.to_rgba('g')) - ] + mcolors.to_rgba('r')), + ({rcparam: 'inherit', 'axes.edgecolor': 'r'}, + mcolors.to_rgba('r')), + ({rcparam: 'g', 'axes.facecolor': 'r'}, + mcolors.to_rgba('g'))] for rc_dict, target in test_values: yield _legend_rcparam_helper, rc_dict, target, get_func diff --git a/lib/mpl_toolkits/mplot3d/art3d.py b/lib/mpl_toolkits/mplot3d/art3d.py index 027d6a07eb76..b8a4619959ef 100755 --- a/lib/mpl_toolkits/mplot3d/art3d.py +++ b/lib/mpl_toolkits/mplot3d/art3d.py @@ -389,12 +389,12 @@ def do_3d_projection(self, renderer): fcs = (zalpha(self._facecolor3d, vzs) if self._depthshade else self._facecolor3d) - fcs = mcolors.colorConverter.to_rgba_array(fcs, self._alpha) + fcs = mcolors.to_rgba_array(fcs, self._alpha) self.set_facecolors(fcs) ecs = (zalpha(self._edgecolor3d, vzs) if self._depthshade else self._edgecolor3d) - ecs = mcolors.colorConverter.to_rgba_array(ecs, self._alpha) + ecs = mcolors.to_rgba_array(ecs, self._alpha) self.set_edgecolors(ecs) PatchCollection.set_offsets(self, list(zip(vxs, vys))) @@ -457,12 +457,12 @@ def do_3d_projection(self, renderer): fcs = (zalpha(self._facecolor3d, vzs) if self._depthshade else self._facecolor3d) - fcs = mcolors.colorConverter.to_rgba_array(fcs, self._alpha) + fcs = mcolors.to_rgba_array(fcs, self._alpha) self.set_facecolors(fcs) ecs = (zalpha(self._edgecolor3d, vzs) if self._depthshade else self._edgecolor3d) - ecs = mcolors.colorConverter.to_rgba_array(ecs, self._alpha) + ecs = mcolors.to_rgba_array(ecs, self._alpha) self.set_edgecolors(ecs) PathCollection.set_offsets(self, list(zip(vxs, vys))) @@ -684,12 +684,12 @@ def set_alpha(self, alpha): raise TypeError('alpha must be a float or None') artist.Artist.set_alpha(self, alpha) try: - self._facecolors = mcolors.colorConverter.to_rgba_array( + self._facecolors = mcolors.to_rgba_array( self._facecolors3d, self._alpha) except (AttributeError, TypeError, IndexError): pass try: - self._edgecolors = mcolors.colorConverter.to_rgba_array( + self._edgecolors = mcolors.to_rgba_array( self._edgecolors3d, self._alpha) except (AttributeError, TypeError, IndexError): pass @@ -764,7 +764,7 @@ def get_colors(c, num): """Stretch the color argument to provide the required number num""" if type(c) == type("string"): - c = mcolors.colorConverter.to_rgba(c) + c = mcolors.to_rgba(c) if iscolor(c): return [c] * num diff --git a/lib/mpl_toolkits/mplot3d/axes3d.py b/lib/mpl_toolkits/mplot3d/axes3d.py index c88f755e6dd7..cc048a034bcd 100755 --- a/lib/mpl_toolkits/mplot3d/axes3d.py +++ b/lib/mpl_toolkits/mplot3d/axes3d.py @@ -29,7 +29,8 @@ import matplotlib.scale as mscale from matplotlib.tri.triangulation import Triangulation import numpy as np -from matplotlib.colors import Normalize, colorConverter, LightSource +from matplotlib import colors as mcolors +from matplotlib.colors import Normalize, LightSource from . import art3d from . import proj3d @@ -1595,7 +1596,7 @@ def plot_surface(self, X, Y, Z, *args, **kwargs): color = kwargs.pop('color', None) if color is None: color = self._get_lines.get_next_color() - color = np.array(colorConverter.to_rgba(color)) + color = np.array(mcolors.to_rgba(color)) fcolors = None cmap = kwargs.get('cmap', None) @@ -1713,7 +1714,7 @@ def _shade_colors(self, color, normals): if len(shade[mask]) > 0: norm = Normalize(min(shade[mask]), max(shade[mask])) shade[~mask] = min(shade[mask]) - color = colorConverter.to_rgba_array(color) + color = mcolors.to_rgba_array(color) # shape of color should be (M, 4) (where M is number of faces) # shape of shade should be (M,) # colors should have final shape of (M, 4) @@ -1866,7 +1867,7 @@ def plot_trisurf(self, *args, **kwargs): color = kwargs.pop('color', None) if color is None: color = self._get_lines.get_next_color() - color = np.array(colorConverter.to_rgba(color)) + color = np.array(mcolors.to_rgba(color)) cmap = kwargs.get('cmap', None) norm = kwargs.pop('norm', None) @@ -2448,7 +2449,7 @@ def bar3d(self, x, y, z, dx, dy, dz, color=None, facecolors.extend([c] * 6) else: # a single color specified, or face colors specified explicitly - facecolors = list(colorConverter.to_rgba_array(color)) + facecolors = list(mcolors.to_rgba_array(color)) if len(facecolors) < len(x): facecolors *= (6 * len(x)) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy