import functools, numbers, re
from . import names
from . import palette
[docs]@functools.singledispatch
def color(c):
raise ValueError('Don\'t understand color name "%s"' % c, _COLOR_USAGE)
@color.register(tuple)
def _(c):
if len(c) != 3:
raise ValueError('Length %d is not 3' % len(c), _COLOR_USAGE)
if not all(0 <= x <= 255 for x in c):
raise ValueError(
'All components must be between 0 and 255', _COLOR_USAGE)
return c
@color.register(list)
def _(c):
return color(tuple(c))
@color.register(numbers.Number)
def _(c):
if not (0 <= c <= 255):
raise ValueError(
'All components must be between 0 and 255', _COLOR_USAGE)
return (c, c, c)
@color.register(str)
def _(c):
try:
return names.name_to_color(c)
except:
raise ValueError('Don\'t understand color name "%s"' % c, _COLOR_USAGE)
[docs]@functools.singledispatch
def colors(c):
"""Return a color Palette"""
raise ValueError("Don't understand type %s" % type(c), _COLORS_USAGE)
@colors.register(palette.Palette)
def _(c):
return c
@colors.register(tuple)
@colors.register(list)
def _(c):
return _colors(c)
@colors.register(str)
def _(s):
return _colors(s)
@colors.register(dict)
def _(d):
return _colors(**d)
@colors.register(type(None))
def _(d):
return _colors()
[docs]def to_triplets(colors):
"""
Coerce a list into a list of triplets.
If `colors` is a list of lists or strings, return it as is. Otherwise,
divide it into tuplets of length three, silently discarding any extra
elements beyond a multiple of three.
"""
try:
colors[0][0]
return colors
except:
pass
# It's a 1-dimensional list
extra = len(colors) % 3
if extra:
colors = colors[:-extra]
return list(zip(*[iter(colors)] * 3))
def _colors(colors=None, **kwds):
from . import palettes
p = palettes.get(colors)
if not p:
return colors_no_palette(colors, **kwds)
if not kwds:
return p
kwds = dict(vars(p), **kwds)
return palette.Palette(p, **kwds)
[docs]def colors_no_palette(colors=None, **kwds):
"""Return a Palette but don't take into account Pallete Names."""
if isinstance(colors, str):
colors = _split_colors(colors)
else:
colors = to_triplets(colors or ())
colors = (color(c) for c in colors or ())
return palette.Palette(colors, **kwds)
def _split_colors(colors):
brackets = False
for part in _COLOR_SPLIT_RE.split(colors):
if brackets:
yield part
else:
part = [p.strip() for p in part.split(',')]
part = [p for p in part if p]
yield from part
brackets = not brackets
_COLOR_SPLIT_RE = re.compile(r'( [([] .*? [])] )', re.X)
_COLOR_USAGE = """
A Color can be initialized with:
* A list of three numbers: [0, 0, 0] or [255, 0, 255].
* A single number which represents a brightness/gray level: 0, 255, 127
* A string: "red", "yellow", "gold" naming a color from ...colors.COLORS.
All numbers should be in the range [0, 256) - 0 <= x <= 255"""
_COLORS_USAGE = """
Colors is a list of colors. Each color can be:
* A list of three numbers: [0, 0, 0] or [255, 0, 255].
* A single number which represents a brightness/gray level: 0, 255, 127
* A string: "red", "yellow", "gold" naming a color from ...colors.COLORS.
All numbers must be in the range [0, 256) - 0 <= x <= 255"""