Source code for pygal.colors

# -*- coding: utf-8 -*-
# This file is part of pygal
#
# A python svg graph plotting library
# Copyright © 2012-2016 Kozea
#
# This library is free software: you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option) any
# later version.
#
# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see <http://www.gnu.org/licenses/>.
"""
This package is an utility package oriented on color alteration.
This is used by the :py:mod:`pygal.style` package to generate
parametric styles.

"""
from __future__ import division


[docs]def normalize_float(f): """Round float errors""" if abs(f - round(f)) < .0000000000001: return round(f) return f
[docs]def rgb_to_hsl(r, g, b): """Convert a color in r, g, b to a color in h, s, l""" r = r or 0 g = g or 0 b = b or 0 r /= 255 g /= 255 b /= 255 max_ = max((r, g, b)) min_ = min((r, g, b)) d = max_ - min_ if not d: h = 0 elif r is max_: h = 60 * (g - b) / d elif g is max_: h = 60 * (b - r) / d + 120 else: h = 60 * (r - g) / d + 240 l = .5 * (max_ + min_) if not d: s = 0 elif l < 0.5: s = .5 * d / l else: s = .5 * d / (1 - l) return tuple(map(normalize_float, (h % 360, s * 100, l * 100)))
[docs]def hsl_to_rgb(h, s, l): """Convert a color in h, s, l to a color in r, g, b""" h /= 360 s /= 100 l /= 100 m2 = l * (s + 1) if l <= .5 else l + s - l * s m1 = 2 * l - m2 def h_to_rgb(h): h = h % 1 if 6 * h < 1: return m1 + 6 * h * (m2 - m1) if 2 * h < 1: return m2 if 3 * h < 2: return m1 + 6 * (2 / 3 - h) * (m2 - m1) return m1 r, g, b = map(lambda x: round(x * 255), map(h_to_rgb, (h + 1 / 3, h, h - 1 / 3))) return r, g, b
[docs]def parse_color(color): """Take any css color definition and give back a tuple containing the r, g, b, a values along with a type which can be: #rgb, #rgba, #rrggbb, #rrggbbaa, rgb, rgba """ r = g = b = a = type = None if color.startswith('#'): color = color[1:] if len(color) == 3: type = '#rgb' color = color + 'f' if len(color) == 4: type = type or '#rgba' color = ''.join([c * 2 for c in color]) if len(color) == 6: type = type or '#rrggbb' color = color + 'ff' assert len(color) == 8 type = type or '#rrggbbaa' r, g, b, a = [ int(''.join(c), 16) for c in zip(color[::2], color[1::2])] a /= 255 elif color.startswith('rgb('): type = 'rgb' color = color[4:-1] r, g, b, a = [int(c) for c in color.split(',')] + [1] elif color.startswith('rgba('): type = 'rgba' color = color[5:-1] r, g, b, a = [int(c) for c in color.split(',')[:-1]] + [ float(color.split(',')[-1])] return r, g, b, a, type
[docs]def unparse_color(r, g, b, a, type): """ Take the r, g, b, a color values and give back a type css color string. This is the inverse function of parse_color """ if type == '#rgb': # Don't lose precision on rgb shortcut if r % 17 == 0 and g % 17 == 0 and b % 17 == 0: return '#%x%x%x' % (int(r / 17), int(g / 17), int(b / 17)) type = '#rrggbb' if type == '#rgba': if r % 17 == 0 and g % 17 == 0 and b % 17 == 0: return '#%x%x%x%x' % (int(r / 17), int(g / 17), int(b / 17), int(a * 15)) type = '#rrggbbaa' if type == '#rrggbb': return '#%02x%02x%02x' % (r, g, b) if type == '#rrggbbaa': return '#%02x%02x%02x%02x' % (r, g, b, int(a * 255)) if type == 'rgb': return 'rgb(%d, %d, %d)' % (r, g, b) if type == 'rgba': return 'rgba(%d, %d, %d, %g)' % (r, g, b, a)
[docs]def is_foreground_light(color): """ Determine if the background color need a light or dark foreground color """ return rgb_to_hsl(*parse_color(color)[:3])[2] < 17.9
_clamp = lambda x: max(0, min(100, x)) def _adjust(hsl, attribute, percent): """Internal adjust function""" hsl = list(hsl) if attribute > 0: hsl[attribute] = _clamp(hsl[attribute] + percent) else: hsl[attribute] += percent return hsl
[docs]def adjust(color, attribute, percent): """Adjust an attribute of color by a percent""" r, g, b, a, type = parse_color(color) r, g, b = hsl_to_rgb(*_adjust(rgb_to_hsl(r, g, b), attribute, percent)) return unparse_color(r, g, b, a, type)
[docs]def rotate(color, percent): """Rotate a color by changing its hue value by percent""" return adjust(color, 0, percent)
[docs]def saturate(color, percent): """Saturate a color by increasing its saturation by percent""" return adjust(color, 1, percent)
[docs]def desaturate(color, percent): """Desaturate a color by decreasing its saturation by percent""" return adjust(color, 1, -percent)
[docs]def lighten(color, percent): """Lighten a color by increasing its lightness by percent""" return adjust(color, 2, percent)
[docs]def darken(color, percent): """Darken a color by decreasing its lightness by percent""" return adjust(color, 2, -percent)