"""Color-manipulating utility routines.
Convenient functions for converting between RGB and HSV color spaces,
formatting colors for use in HTML/CSS, and generating a series of
(relatively) contrasting hues.
"""
import math
from itertools import islice
from ll.xist import xsc
from ll.xist.ns import html
[docs]def rgb2hsv (rgb):
"""Convert RGB color to HSV.
:param rgb: a 3-tuple containing the R, G, and B values.
:return: the HSV representation of the color as a 3-tuple.
"""
r, g, b = rgb
_min = min (r, g, b)
_max = max (r, g, b)
if _max == 0:
return 0, 0, 0
delta = _max - _min
s = delta / _max
v = _max
if r == _max:
h = (g - b) / delta
elif g == _max:
h = 2 + (b - r) / delta
elif b == _max:
h = 4 + (r - g) / delta
else:
raise
return h * 60, s, v
[docs]def hsv2rgb (hsv):
"""Convert HSV color to RGB.
:param hsv: a 3-tuple containing the H, S, and V values.
:return: the RGB representation of the color as a 3-tuple.
"""
h, s, v = hsv
h /= 60
i = math.floor (h)
f = h - i
p = v * (1 - s)
q = v * (1 - s * f)
t = v * (1 - s * (1 - f))
i %= 6
if i == 0:
return v, t, p
if i == 1:
return q, v, p
if i == 2:
return p, v, t
if i == 3:
return p, q, v
if i == 4:
return t, p, v
if i == 5:
return v, p, q
[docs]def rgb2html (rgb):
"""Write an RGB color in HTML representation.
:param rgb: a 3-tuple containing the R, G, and B values.
:return: HTML/CSS attribute value representing the color.
"""
r, g, b = rgb
r = round (r * 255)
g = round (g * 255)
b = round (b * 255)
return '#%02X%02X%02X' % (r, g, b)
[docs]def hues (start=0):
"""Generate a sequence of hues intended to be contrasting.
:param start: the starting point in the sequence of colors.
:return: a generator which returns successive hue values.
The hues start out with red (assuming the default start value is used) and
rotate around the spectrum spaced out so that the set of hues returned up
to any point is evenly distributed according to hue values. No attempt is
made to optimize the distribution for human color perception, i.e.,
filling in some parts of the spectrum more or less densely to improve the
human-visible contrast.
"""
step = 360 / ((1 + math.sqrt (5)) / 2)
h = start
while True:
yield h
h += step
[docs]def html_row (i, hsv):
"""Create a table row to demonstrate a hue.
:param int i: the number of the row within the table, for inclusion in the
first column.
:param hsv: the color to demonstrate.
:return: an HTML <tr> element showing the color, as well as related colors
with 70% and 40% of the saturation of the given color.
For use by :func:`html_sample`.
"""
h, s, v = hsv
main_colour = rgb2html (hsv2rgb ((h, s, v)))
second_colour = rgb2html (hsv2rgb ((h, s * 0.7, v)))
third_colour = rgb2html (hsv2rgb ((h, s * 0.4, v)))
return html.tr (
html.td (i, ' ', int (h) % 360),
html.td (main_colour, style="background-color: %s" % main_colour),
html.td (second_colour, style="background-color: %s" % second_colour),
html.td (third_colour, style="background-color: %s" % third_colour),
)
[docs]def html_sample (n):
"""Create a sample HTML page showing hues generated by :func:`hues`.
:param int n: the number of hues to generate.
:return: an HTML document containing a table with one row per hue.
"""
return xsc.Frag (
html.DocTypeHTML401transitional (),
html.html (
html.head (html.title ()),
html.body (
html.table (
html_row (i, base_colour)
for i, base_colour in enumerate (islice (hues (), n))
)
)
)
)