From b86fc830b9ff804935c26c8af3967cca232ce063 Mon Sep 17 00:00:00 2001 From: Python3pkg Date: Sun, 21 May 2017 06:04:57 -0700 Subject: [PATCH 1/2] Convert to Python3 --- grapefruit.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/grapefruit.py b/grapefruit.py index ecf5184..44d8098 100644 --- a/grapefruit.py +++ b/grapefruit.py @@ -17,7 +17,7 @@ """GrapeFruit - Color manipulation in Python""" -from __future__ import division + import sys @@ -1492,7 +1492,7 @@ def __unicode__(self): The RGBA representation of this grapefruit.Color instance. """ - return unicode("({}, {}, {}, {})".format(*[round(v, 6) for v in (self.__rgb + (self.__a,))])) + return str("({}, {}, {}, {})".format(*[round(v, 6) for v in (self.__rgb + (self.__a,))])) def __iter__(self): return iter(self.__rgb + (self.__a,)) From 91e1940f870811eaa52c6e2134b34317ddc05940 Mon Sep 17 00:00:00 2001 From: Python3pkg Date: Sun, 21 May 2017 06:06:30 -0700 Subject: [PATCH 2/2] Convert to Python3 --- build/lib/grapefruit.py | 2193 ++++++++++++++++++++++ dist/grapefruit-0.1a4-py3.6.egg | Bin 0 -> 32968 bytes grapefruit.egg-info/PKG-INFO | 163 ++ grapefruit.egg-info/SOURCES.txt | 9 + grapefruit.egg-info/dependency_links.txt | 1 + grapefruit.egg-info/requires.txt | 3 + grapefruit.egg-info/top_level.txt | 1 + 7 files changed, 2370 insertions(+) create mode 100644 build/lib/grapefruit.py create mode 100644 dist/grapefruit-0.1a4-py3.6.egg create mode 100644 grapefruit.egg-info/PKG-INFO create mode 100644 grapefruit.egg-info/SOURCES.txt create mode 100644 grapefruit.egg-info/dependency_links.txt create mode 100644 grapefruit.egg-info/requires.txt create mode 100644 grapefruit.egg-info/top_level.txt diff --git a/build/lib/grapefruit.py b/build/lib/grapefruit.py new file mode 100644 index 0000000..44d8098 --- /dev/null +++ b/build/lib/grapefruit.py @@ -0,0 +1,2193 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*-# + +# Copyright (c) 2008-2016, Xavier Basty +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""GrapeFruit - Color manipulation in Python""" + + + +import sys + +_oneThird = 1.0 / 3 +_srgbGammaCorrInv = 0.03928 / 12.92 +_sixteenHundredsixteenth = 16.0 / 116 + +_RybWheel = ( + 0, 26, 52, + 83, 120, 130, + 141, 151, 162, + 177, 190, 204, + 218, 232, 246, + 261, 275, 288, + 303, 317, 330, + 338, 345, 352, + 360) + +_RgbWheel = ( + 0, 8, 17, + 26, 34, 41, + 48, 54, 60, + 81, 103, 123, + 138, 155, 171, + 187, 204, 219, + 234, 251, 267, + 282, 298, 329, + 360) + +# Tristumulus values calculated from the CIE chromaticity coordinates on +# https://en.wikipedia.org/wiki/Standard_illuminant#White_points_of_standard_illuminants +WHITE_REFERENCE = { + # 2° (CIE 1931) + 'std_A' : (1.098470, 1.000000, 0.355823), # 2856K, Incandescent / Tungsten + 'std_B' : (0.990927, 1.000000, 0.853133), # 4874K, Direct sunlight at noon (obsolete) + 'std_C' : (0.980706, 1.000000, 1.182250), # 6774K, Average / North sky Daylight (obsolete) + 'std_D50' : (0.964212, 1.000000, 0.825188), # 5003K, Horizon Light + 'std_D55' : (0.956797, 1.000000, 0.921481), # 5503K, Mid-morning / Mid-afternoon Daylight + 'std_D65' : (0.950429, 1.000000, 1.088900), # 6504K, Noon Daylight (Television, sRGB color space) + 'std_D75' : (0.949722, 1.000000, 1.226390), # 7504K, North sky Daylight + 'std_E' : (1.000000, 1.000000, 1.000000), # 5454K, Equal energy + 'std_F1' : (0.928336, 1.000000, 1.036650), # 6430K, Daylight Fluorescent + 'std_F2' : (0.991447, 1.000000, 0.673159), # 4230K, Cool White Fluorescent + 'std_F3' : (1.037530, 1.000000, 0.498605), # 3450K, White Fluorescent + 'std_F4' : (1.091470, 1.000000, 0.388133), # 2940K, Warm White Fluorescent + 'std_F5' : (0.908720, 1.000000, 0.987229), # 6350K, Daylight Fluorescent + 'std_F6' : (0.973091, 1.000000, 0.601905), # 4150K, Lite White Fluorescent + 'std_F7' : (0.950172, 1.000000, 1.086300), # 6500K, D65 simulator, Daylight simulator + 'std_F8' : (0.964125, 1.000000, 0.823331), # 5000K, D50 simulator, Sylvania F40 Design 50 + 'std_F9' : (1.003650, 1.000000, 0.678684), # 4150K, Cool White Deluxe Fluorescent + 'std_F10' : (0.961735, 1.000000, 0.817123), # 5000K, Philips TL85, Ultralume 50 + 'std_F11' : (1.008990, 1.000000, 0.642617), # 4000K, Philips TL84, Ultralume 40 + 'std_F12' : (1.080460, 1.000000, 0.392275), # 3000K, Philips TL83, Ultralume 30 + # 10° (CIE 1964) + 'sup_A' : (1.111420, 1.000000, 0.351998), + 'sup_B' : (0.991778, 1.000000, 0.843493), + 'sup_C' : (0.972857, 1.000000, 1.161450), + 'sup_D50' : (0.967206, 1.000000, 0.814280), + 'sup_D55' : (0.957967, 1.000000, 0.909253), + 'sup_D65' : (0.948097, 1.000000, 1.073050), + 'sup_D75' : (0.944171, 1.000000, 1.206430), + 'sup_E' : (1.000000, 1.000000, 1.000000), + 'sup_F1' : (0.947913, 1.000000, 1.031910), + 'sup_F2' : (1.032450, 1.000000, 0.689897), + 'sup_F3' : (1.089680, 1.000000, 0.519648), + 'sup_F4' : (1.149610, 1.000000, 0.409633), + 'sup_F5' : (0.933686, 1.000000, 0.986363), + 'sup_F6' : (1.021480, 1.000000, 0.620736), + 'sup_F7' : (0.957797, 1.000000, 1.076180), + 'sup_F8' : (0.971146, 1.000000, 0.811347), + 'sup_F9' : (1.021160, 1.000000, 0.678256), + 'sup_F10' : (0.990012, 1.000000, 0.831340), + 'sup_F11' : (1.038200, 1.000000, 0.655550), + 'sup_F12' : (1.114280, 1.000000, 0.403530)} + +# The default white reference, use 2° Standard Observer, D65 (daylight) +_DEFAULT_WREF = WHITE_REFERENCE['std_D65'] + + +NAMED_COLOR = { + 'aliceblue': '#f0f8ff', + 'antiquewhite': '#faebd7', + 'aqua': '#00ffff', + 'aquamarine': '#7fffd4', + 'azure': '#f0ffff', + 'beige': '#f5f5dc', + 'bisque': '#ffe4c4', + 'black': '#000000', + 'blanchedalmond': '#ffebcd', + 'blue': '#0000ff', + 'blueviolet': '#8a2be2', + 'brown': '#a52a2a', + 'burlywood': '#deb887', + 'cadetblue': '#5f9ea0', + 'chartreuse': '#7fff00', + 'chocolate': '#d2691e', + 'coral': '#ff7f50', + 'cornflowerblue': '#6495ed', + 'cornsilk': '#fff8dc', + 'crimson': '#dc143c', + 'cyan': '#00ffff', + 'darkblue': '#00008b', + 'darkcyan': '#008b8b', + 'darkgoldenrod': '#b8860b', + 'darkgray': '#a9a9a9', + 'darkgrey': '#a9a9a9', + 'darkgreen': '#006400', + 'darkkhaki': '#bdb76b', + 'darkmagenta': '#8b008b', + 'darkolivegreen': '#556b2f', + 'darkorange': '#ff8c00', + 'darkorchid': '#9932cc', + 'darkred': '#8b0000', + 'darksalmon': '#e9967a', + 'darkseagreen': '#8fbc8f', + 'darkslateblue': '#483d8b', + 'darkslategray': '#2f4f4f', + 'darkslategrey': '#2f4f4f', + 'darkturquoise': '#00ced1', + 'darkviolet': '#9400d3', + 'deeppink': '#ff1493', + 'deepskyblue': '#00bfff', + 'dimgray': '#696969', + 'dimgrey': '#696969', + 'dodgerblue': '#1e90ff', + 'firebrick': '#b22222', + 'floralwhite': '#fffaf0', + 'forestgreen': '#228b22', + 'fuchsia': '#ff00ff', + 'gainsboro': '#dcdcdc', + 'ghostwhite': '#f8f8ff', + 'gold': '#ffd700', + 'goldenrod': '#daa520', + 'gray': '#808080', + 'grey': '#808080', + 'green': '#008000', + 'greenyellow': '#adff2f', + 'honeydew': '#f0fff0', + 'hotpink': '#ff69b4', + 'indianred': '#cd5c5c', + 'indigo': '#4b0082', + 'ivory': '#fffff0', + 'khaki': '#f0e68c', + 'lavender': '#e6e6fa', + 'lavenderblush': '#fff0f5', + 'lawngreen': '#7cfc00', + 'lemonchiffon': '#fffacd', + 'lightblue': '#add8e6', + 'lightcoral': '#f08080', + 'lightcyan': '#e0ffff', + 'lightgoldenrodyellow': '#fafad2', + 'lightgreen': '#90ee90', + 'lightgray': '#d3d3d3', + 'lightgrey': '#d3d3d3', + 'lightpink': '#ffb6c1', + 'lightsalmon': '#ffa07a', + 'lightseagreen': '#20b2aa', + 'lightskyblue': '#87cefa', + 'lightslategray': '#778899', + 'lightslategrey': '#778899', + 'lightsteelblue': '#b0c4de', + 'lightyellow': '#ffffe0', + 'lime': '#00ff00', + 'limegreen': '#32cd32', + 'linen': '#faf0e6', + 'magenta': '#ff00ff', + 'maroon': '#800000', + 'mediumaquamarine': '#66cdaa', + 'mediumblue': '#0000cd', + 'mediumorchid': '#ba55d3', + 'mediumpurple': '#9370db', + 'mediumseagreen': '#3cb371', + 'mediumslateblue': '#7b68ee', + 'mediumspringgreen': '#00fa9a', + 'mediumturquoise': '#48d1cc', + 'mediumvioletred': '#c71585', + 'midnightblue': '#191970', + 'mintcream': '#f5fffa', + 'mistyrose': '#ffe4e1', + 'moccasin': '#ffe4b5', + 'navajowhite': '#ffdead', + 'navy': '#000080', + 'oldlace': '#fdf5e6', + 'olive': '#808000', + 'olivedrab': '#6b8e23', + 'orange': '#ffa500', + 'orangered': '#ff4500', + 'orchid': '#da70d6', + 'palegoldenrod': '#eee8aa', + 'palegreen': '#98fb98', + 'paleturquoise': '#afeeee', + 'palevioletred': '#db7093', + 'papayawhip': '#ffefd5', + 'peachpuff': '#ffdab9', + 'peru': '#cd853f', + 'pink': '#ffc0cb', + 'plum': '#dda0dd', + 'powderblue': '#b0e0e6', + 'purple': '#800080', + 'red': '#ff0000', + 'rosybrown': '#bc8f8f', + 'royalblue': '#4169e1', + 'saddlebrown': '#8b4513', + 'salmon': '#fa8072', + 'sandybrown': '#f4a460', + 'seagreen': '#2e8b57', + 'seashell': '#fff5ee', + 'sienna': '#a0522d', + 'silver': '#c0c0c0', + 'skyblue': '#87ceeb', + 'slateblue': '#6a5acd', + 'slategray': '#708090', + 'slategrey': '#708090', + 'snow': '#fffafa', + 'springgreen': '#00ff7f', + 'steelblue': '#4682b4', + 'tan': '#d2b48c', + 'teal': '#008080', + 'thistle': '#d8bfd8', + 'tomato': '#ff6347', + 'turquoise': '#40e0d0', + 'violet': '#ee82ee', + 'wheat': '#f5deb3', + 'white': '#ffffff', + 'whitesmoke': '#f5f5f5', + 'yellow': '#ffff00', + 'yellowgreen': '#9acd32'} + +def rgb_to_hsl(r, g=None, b=None): + """Convert the color from RGB coordinates to HSL. + + Parameters: + :r: + The Red component value [0...1] + :g: + The Green component value [0...1] + :b: + The Blue component value [0...1] + + Returns: + The color as an (h, s, l) tuple in the range: + h[0...360], + s[0...1], + l[0...1] + + >>> rgb_to_hsl(1, 0.5, 0) + (30.0, 1.0, 0.5) + + """ + if type(r) in [list,tuple]: + r, g, b = r + + minVal = min(r, g, b) # min RGB value + maxVal = max(r, g, b) # max RGB value + + l = (maxVal + minVal) / 2.0 + if minVal==maxVal: + return (0.0, 0.0, l) # achromatic (gray) + + d = maxVal - minVal # delta RGB value + + if l < 0.5: s = d / (maxVal + minVal) + else: s = d / (2.0 - maxVal - minVal) + + dr, dg, db = [(maxVal-val) / d for val in (r, g, b)] + + if r==maxVal: + h = db - dg + elif g==maxVal: + h = 2.0 + dr - db + else: + h = 4.0 + dg - dr + + h = (h*60.0) % 360.0 + return (h, s, l) + +def _hue_to_rgb(n1, n2=None, h=None): + if type(n1) in [list,tuple]: + n1, n2, h = n1 + + h %= 6.0 + if h < 1.0: return n1 + ((n2-n1) * h) + if h < 3.0: return n2 + if h < 4.0: return n1 + ((n2-n1) * (4.0 - h)) + return n1 + +def hsl_to_rgb(h, s=None, l=None): + """Convert the color from HSL coordinates to RGB. + + Parameters: + :h: + The Hue component value [0...1] + :s: + The Saturation component value [0...1] + :l: + The Lightness component value [0...1] + + Returns: + The color as an (r, g, b) tuple in the range: + r[0...1], + g[0...1], + b[0...1] + + >>> hsl_to_rgb(30.0, 1.0, 0.5) + (1.0, 0.5, 0.0) + + """ + if type(h) in [list,tuple]: + h, s, l = h + + if s==0: return (l, l, l) # achromatic (gray) + + if l<0.5: n2 = l * (1.0 + s) + else: n2 = l+s - (l*s) + + n1 = (2.0 * l) - n2 + + h /= 60.0 + hueToRgb = _hue_to_rgb + r = hueToRgb(n1, n2, h + 2) + g = hueToRgb(n1, n2, h) + b = hueToRgb(n1, n2, h - 2) + + return (r, g, b) + +def rgb_to_hsv(r, g=None, b=None): + """Convert the color from RGB coordinates to HSV. + + Parameters: + :r: + The Red component value [0...1] + :g: + The Green component value [0...1] + :b: + The Blue component value [0...1] + + Returns: + The color as an (h, s, v) tuple in the range: + h[0...360], + s[0...1], + v[0...1] + + >>> rgb_to_hsv(1, 0.5, 0) + (30.0, 1.0, 1.0) + + """ + if type(r) in [list,tuple]: + r, g, b = r + + v = float(max(r, g, b)) + d = v - min(r, g, b) + if d==0: return (0.0, 0.0, v) + s = d / v + + dr, dg, db = [(v - val) / d for val in (r, g, b)] + + if r==v: + h = db - dg # between yellow & magenta + elif g==v: + h = 2.0 + dr - db # between cyan & yellow + else: # b==v + h = 4.0 + dg - dr # between magenta & cyan + + h = (h*60.0) % 360.0 + return (h, s, v) + +def hsv_to_rgb(h, s=None, v=None): + """Convert the color from RGB coordinates to HSV. + + Parameters: + :h: + The Hus component value [0...1] + :s: + The Saturation component value [0...1] + :v: + The Value component [0...1] + + Returns: + The color as an (r, g, b) tuple in the range: + r[0...1], + g[0...1], + b[0...1] + + >>> hsv_to_rgb(30.0, 1.0, 0.5) + (0.5, 0.25, 0.0) + + """ + if type(h) in [list,tuple]: + h, s, v = h + + if s==0: return (v, v, v) # achromatic (gray) + + h /= 60.0 + h = h % 6.0 + + i = int(h) + f = h - i + if not(i&1): f = 1-f # if i is even + + m = v * (1.0 - s) + n = v * (1.0 - (s * f)) + + if i==0: return (v, n, m) + if i==1: return (n, v, m) + if i==2: return (m, v, n) + if i==3: return (m, n, v) + if i==4: return (n, m, v) + return (v, m, n) + +def rgb_to_yiq(r, g=None, b=None): + """Convert the color from RGB to YIQ. + + Parameters: + :r: + The Red component value [0...1] + :g: + The Green component value [0...1] + :b: + The Blue component value [0...1] + + Returns: + The color as an (y, i, q) tuple in the range: + y[0...1], + i[0...1], + q[0...1] + + >>> '(%g, %g, %g)' % rgb_to_yiq(1, 0.5, 0) + '(0.592263, 0.458874, -0.0499818)' + + """ + if type(r) in [list,tuple]: + r, g, b = r + + y = (r * 0.29895808) + (g * 0.58660979) + (b *0.11443213) + i = (r * 0.59590296) - (g * 0.27405705) - (b *0.32184591) + q = (r * 0.21133576) - (g * 0.52263517) + (b *0.31129940) + return (y, i, q) + +def yiq_to_rgb(y, i=None, q=None): + """Convert the color from YIQ coordinates to RGB. + + Parameters: + :y: + Tte Y component value [0...1] + :i: + The I component value [0...1] + :q: + The Q component value [0...1] + + Returns: + The color as an (r, g, b) tuple in the range: + r[0...1], + g[0...1], + b[0...1] + + >>> '({}, {}, {})'.format(*[round(v, 6) for v in yiq_to_rgb(0.592263, 0.458874, -0.0499818)]) + '(1.0, 0.5, 1e-06)' + + """ + if type(y) in [list,tuple]: + y, i, q = y + r = y + (i * 0.9562) + (q * 0.6210) + g = y - (i * 0.2717) - (q * 0.6485) + b = y - (i * 1.1053) + (q * 1.7020) + return (r, g, b) + +def rgb_to_yuv(r, g=None, b=None): + """Convert the color from RGB coordinates to YUV. + + Parameters: + :r: + The Red component value [0...1] + :g: + The Green component value [0...1] + :b: + The Blue component value [0...1] + + Returns: + The color as an (y, u, v) tuple in the range: + y[0...1], + u[-0.436...0.436], + v[-0.615...0.615] + + >>> '(%g, %g, %g)' % rgb_to_yuv(1, 0.5, 0) + '(0.5925, -0.29156, 0.357505)' + + """ + if type(r) in [list,tuple]: + r, g, b = r + + y = (r * 0.29900) + (g * 0.58700) + (b * 0.11400) + u = -(r * 0.14713) - (g * 0.28886) + (b * 0.43600) + v = (r * 0.61500) - (g * 0.51499) - (b * 0.10001) + return (y, u, v) + +def yuv_to_rgb(y, u=None, v=None): + """Convert the color from YUV coordinates to RGB. + + Parameters: + :y: + The Y component value [0...1] + :u: + The U component value [-0.436...0.436] + :v: + The V component value [-0.615...0.615] + + Returns: + The color as an (r, g, b) tuple in the range: + r[0...1], + g[0...1], + b[0...1] + + >>> '(%g, %g, %g)' % yuv_to_rgb(0.5925, -0.2916, 0.3575) + '(0.999989, 0.500015, -6.3276e-05)' + + """ + if type(y) in [list,tuple]: + y, u, v = y + r = y + (v * 1.13983) + g = y - (u * 0.39465) - (v * 0.58060) + b = y + (u * 2.03211) + return (r, g, b) + +def rgb_to_xyz(r, g=None, b=None): + """Convert the color from sRGB to CIE XYZ. + + The methods assumes that the RGB coordinates are given in the sRGB + colorspace (D65). + + .. note:: + + Compensation for the sRGB gamma correction is applied before converting. + + Parameters: + :r: + The Red component value [0...1] + :g: + The Green component value [0...1] + :b: + The Blue component value [0...1] + + Returns: + The color as an (x, y, z) tuple in the range: + x[0...1], + y[0...1], + z[0...1] + + >>> '(%g, %g, %g)' % rgb_to_xyz(1, 0.5, 0) + '(0.488941, 0.365682, 0.0448137)' + + """ + if type(r) in [list,tuple]: + r, g, b = r + + r, g, b = [((v <= 0.03928) and [v / 12.92] or [((v+0.055) / 1.055) **2.4])[0] for v in (r, g, b)] + + x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805) + y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722) + z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505) + return (x, y, z) + +def xyz_to_rgb(x, y=None, z=None): + """Convert the color from CIE XYZ coordinates to sRGB. + + .. note:: + + Compensation for sRGB gamma correction is applied before converting. + + Parameters: + :x: + The X component value [0...1] + :y: + The Y component value [0...1] + :z: + The Z component value [0...1] + + Returns: + The color as an (r, g, b) tuple in the range: + r[0...1], + g[0...1], + b[0...1] + + >>> '(%g, %g, %g)' % xyz_to_rgb(0.488941, 0.365682, 0.0448137) + '(1, 0.5, 6.81883e-08)' + + """ + if type(x) in [list,tuple]: + x, y, z = x + r = (x * 3.2406255) - (y * 1.5372080) - (z * 0.4986286) + g = -(x * 0.9689307) + (y * 1.8757561) + (z * 0.0415175) + b = (x * 0.0557101) - (y * 0.2040211) + (z * 1.0569959) + return tuple((((v <= _srgbGammaCorrInv) and [v * 12.92] or [(1.055 * (v ** (1/2.4))) - 0.055])[0] for v in (r, g, b))) + +def xyz_to_lab(x, y=None, z=None, wref=_DEFAULT_WREF): + """Convert the color from CIE XYZ to CIE L*a*b*. + + Parameters: + :x: + The X component value [0...1] + :y: + The Y component value [0...1] + :z: + The Z component value [0...1] + :wref: + The whitepoint reference, default is 2° D65. + + Returns: + The color as an (L, a, b) tuple in the range: + L[0...100], + a[-1...1], + b[-1...1] + + >>> '(%g, %g, %g)' % xyz_to_lab(0.488941, 0.365682, 0.0448137) + '(66.9518, 0.430841, 0.739692)' + + >>> '(%g, %g, %g)' % xyz_to_lab(0.488941, 0.365682, 0.0448137, WHITE_REFERENCE['std_D50']) + '(66.9518, 0.41166, 0.67282)' + + """ + if type(x) in [list,tuple]: + x, y, z = x + # White point correction + x /= wref[0] + y /= wref[1] + z /= wref[2] + + # Nonlinear distortion and linear transformation + x, y, z = [((v > 0.008856) and [v**_oneThird] or [(7.787 * v) + _sixteenHundredsixteenth])[0] for v in (x, y, z)] + + # Vector scaling + l = (116 * y) - 16 + a = 5.0 * (x - y) + b = 2.0 * (y - z) + + return (l, a, b) + +def lab_to_xyz(l, a=None, b=None, wref=_DEFAULT_WREF): + """Convert the color from CIE L*a*b* to CIE 1931 XYZ. + + Parameters: + :l: + The L component [0...100] + :a: + The a component [-1...1] + :b: + The a component [-1...1] + :wref: + The whitepoint reference, default is 2° D65. + + Returns: + The color as an (x, y, z) tuple in the range: + x[0...q], + y[0...1], + z[0...1] + + >>> '(%g, %g, %g)' % lab_to_xyz(66.9518, 0.43084, 0.739692) + '(0.48894, 0.365682, 0.0448137)' + + >>> '(%g, %g, %g)' % lab_to_xyz(66.9518, 0.411663, 0.67282, WHITE_REFERENCE['std_D50']) + '(0.488942, 0.365682, 0.0448137)' + + """ + if type(l) in [list,tuple]: + l, a, b = l + y = (l + 16) / 116 + x = (a / 5.0) + y + z = y - (b / 2.0) + return tuple((((v > 0.206893) and [v**3] or [(v - _sixteenHundredsixteenth) / 7.787])[0] * w for v, w in zip((x, y, z), wref))) + +def cmyk_to_cmy(c, m=None, y=None, k=None): + """Convert the color from CMYK coordinates to CMY. + + Parameters: + :c: + The Cyan component value [0...1] + :m: + The Magenta component value [0...1] + :y: + The Yellow component value [0...1] + :k: + The Black component value [0...1] + + Returns: + The color as an (c, m, y) tuple in the range: + c[0...1], + m[0...1], + y[0...1] + + >>> '(%g, %g, %g)' % cmyk_to_cmy(1, 0.32, 0, 0.5) + '(1, 0.66, 0.5)' + + """ + if type(c) in [list,tuple]: + c, m, y, k = c + mk = 1-k + return ((c*mk + k), (m*mk + k), (y*mk + k)) + +def cmy_to_cmyk(c, m=None, y=None): + """Convert the color from CMY coordinates to CMYK. + + Parameters: + :c: + The Cyan component value [0...1] + :m: + The Magenta component value [0...1] + :y: + The Yellow component value [0...1] + + Returns: + The color as an (c, m, y, k) tuple in the range: + c[0...1], + m[0...1], + y[0...1], + k[0...1] + + >>> '(%g, %g, %g, %g)' % cmy_to_cmyk(1, 0.66, 0.5) + '(1, 0.32, 0, 0.5)' + + """ + if type(c) in [list,tuple]: + c, m, y = c + k = min(c, m, y) + if k==1.0: return (0.0, 0.0, 0.0, 1.0) + mk = 1.0-k + return ((c-k) / mk, (m-k) / mk, (y-k) / mk, k) + +def rgb_to_cmy(r, g=None, b=None): + """Convert the color from RGB coordinates to CMY. + + Parameters: + :r: + The Red component value [0...1] + :g: + The Green component value [0...1] + :b: + The Blue component value [0...1] + + Returns: + The color as an (c, m, y) tuple in the range: + c[0...1], + m[0...1], + y[0...1] + + >>> rgb_to_cmy(1, 0.5, 0) + (0, 0.5, 1) + + """ + if type(r) in [list,tuple]: + r, g, b = r + return (1-r, 1-g, 1-b) + +def cmy_to_rgb(c, m=None, y=None): + """Convert the color from CMY coordinates to RGB. + + Parameters: + :c: + The Cyan component value [0...1] + :m: + The Magenta component value [0...1] + :y: + The Yellow component value [0...1] + + Returns: + The color as an (r, g, b) tuple in the range: + r[0...1], + g[0...1], + b[0...1] + + >>> cmy_to_rgb(0, 0.5, 1) + (1, 0.5, 0) + + """ + if type(c) in [list,tuple]: + c, m, y = c + return (1-c, 1-m, 1-y) + +def rgb_to_ints(r, g=None, b=None): + """Convert the color in the standard [0...1] range to ints in the [0..255] range. + + Parameters: + :r: + The Red component value [0...1] + :g: + The Green component value [0...1] + :b: + The Blue component value [0...1] + + Returns: + The color as an (r, g, b) tuple in the range: + r[0...255], + g[0...2551], + b[0...2551] + + >>> rgb_to_ints(1, 0.5, 0) + (255, 128, 0) + + """ + if type(r) in [list,tuple]: + r, g, b = r + return tuple(int(round(v*255)) for v in (r, g, b)) + +def ints_to_rgb(r, g=None, b=None): + """Convert ints in the [0...255] range to the standard [0...1] range. + + Parameters: + :r: + The Red component value [0...255] + :g: + The Green component value [0...255] + :b: + The Blue component value [0...255] + + Returns: + The color as an (r, g, b) tuple in the range: + r[0...1], + g[0...1], + b[0...1] + + >>> '(%g, %g, %g)' % ints_to_rgb((255, 128, 0)) + '(1, 0.501961, 0)' + + """ + if type(r) in [list,tuple]: + r, g, b = r + return tuple(float(v) / 255.0 for v in [r, g, b]) + +def rgb_to_html(r, g=None, b=None): + """Convert the color from (r, g, b) to #RRGGBB. + + Parameters: + :r: + The Red component value [0...1] + :g: + The Green component value [0...1] + :b: + The Blue component value [0...1] + + Returns: + A CSS string representation of this color (#RRGGBB). + + >>> rgb_to_html(1, 0.5, 0) + '#ff8000' + + """ + if type(r) in [list,tuple]: + r, g, b = r + return '#%02x%02x%02x' % tuple((min(round(v*255), 255) for v in (r, g, b))) + +def html_to_rgb(html): + """Convert the HTML color to (r, g, b). + + Parameters: + :html: + the HTML definition of the color (#RRGGBB or #RGB or a color name). + + Returns: + The color as an (r, g, b) tuple in the range: + r[0...1], + g[0...1], + b[0...1] + + Throws: + :ValueError: + If html is neither a known color name or a hexadecimal RGB + representation. + + >>> '(%g, %g, %g)' % html_to_rgb('#ff8000') + '(1, 0.501961, 0)' + >>> '(%g, %g, %g)' % html_to_rgb('ff8000') + '(1, 0.501961, 0)' + >>> '(%g, %g, %g)' % html_to_rgb('#f60') + '(1, 0.4, 0)' + >>> '(%g, %g, %g)' % html_to_rgb('f60') + '(1, 0.4, 0)' + >>> '(%g, %g, %g)' % html_to_rgb('lemonchiffon') + '(1, 0.980392, 0.803922)' + + """ + html = html.strip().lower() + if html[0]=='#': + html = html[1:] + elif html in NAMED_COLOR: + html = NAMED_COLOR[html][1:] + + if len(html)==6: + rgb = html[:2], html[2:4], html[4:] + elif len(html)==3: + rgb = ['%c%c' % (v,v) for v in html] + else: + raise ValueError("input #%s is not in #RRGGBB format" % html) + + return tuple(((int(n, 16) / 255.0) for n in rgb)) + +def rgb_to_pil(r, g=None, b=None): + """Convert the color from RGB to a PIL-compatible integer. + + Parameters: + :r: + The Red component value [0...1] + :g: + The Green component value [0...1] + :b: + The Blue component value [0...1] + + Returns: + A PIL compatible integer (0xBBGGRR). + + >>> '0x%06x' % rgb_to_pil(1, 0.5, 0) + '0x0080ff' + + """ + if type(r) in [list,tuple]: + r, g, b = r + r, g, b = [min(int(round(v*255)), 255) for v in (r, g, b)] + return (b << 16) + (g << 8) + r + +def pil_to_rgb(pil): + """Convert the color from a PIL-compatible integer to RGB. + + Parameters: + pil: a PIL compatible color representation (0xBBGGRR) + Returns: + The color as an (r, g, b) tuple in the range: + the range: + r: [0...1] + g: [0...1] + b: [0...1] + + >>> '(%g, %g, %g)' % pil_to_rgb(0x0080ff) + '(1, 0.501961, 0)' + + """ + r = 0xff & pil + g = 0xff & (pil >> 8) + b = 0xff & (pil >> 16) + return tuple((v / 255.0 for v in (r, g, b))) + +def _websafe_component(c, alt=False): + """Convert a color component to its web safe equivalent. + + Parameters: + :c: + The component value [0...1] + :alt: + If True, return the alternative value instead of the nearest one. + + Returns: + The web safe equivalent of the component value. + + """ + # This sucks, but floating point between 0 and 1 is quite fuzzy... + # So we just change the scale a while to make the equality tests + # work, otherwise it gets wrong at some decimal far to the right. + sc = c * 100.0 + + # If the color is already safe, return it straight away + d = sc % 20 + if d==0: return c + + # Get the lower and upper safe values + l = sc - d + u = l + 20 + + # Return the 'closest' value according to the alt flag + if alt: + if (sc-l) >= (u-sc): return l/100.0 + else: return u/100.0 + else: + if (sc-l) >= (u-sc): return u/100.0 + else: return l/100.0 + +def rgb_to_websafe(r, g=None, b=None, alt=False): + """Convert the color from RGB to 'web safe' RGB + + Parameters: + :r: + The Red component value [0...1] + :g: + The Green component value [0...1] + :b: + The Blue component value [0...1] + :alt: + If True, use the alternative color instead of the nearest one. + Can be used for dithering. + + Returns: + The color as an (r, g, b) tuple in the range: + the range: + r[0...1], + g[0...1], + b[0...1] + + >>> '(%g, %g, %g)' % rgb_to_websafe(1, 0.55, 0.0) + '(1, 0.6, 0)' + + """ + if type(r) in [list,tuple]: + r, g, b = r + websafeComponent = _websafe_component + return tuple((websafeComponent(v, alt) for v in (r, g, b))) + +def rgb_to_greyscale(r, g=None, b=None): + """Convert the color from RGB to its greyscale equivalent + + Parameters: + :r: + The Red component value [0...1] + :g: + The Green component value [0...1] + :b: + The Blue component value [0...1] + + Returns: + The color as an (r, g, b) tuple in the range: + the range: + r[0...1], + g[0...1], + b[0...1] + + >>> '(%g, %g, %g)' % rgb_to_greyscale(1, 0.8, 0) + '(0.6, 0.6, 0.6)' + + """ + if type(r) in [list,tuple]: + r, g, b = r + v = (r + g + b) / 3.0 + return (v, v, v) + +def rgb_to_ryb(hue): + """Maps a hue on the RGB color wheel to Itten's RYB wheel. + + Parameters: + :hue: + The hue on the RGB color wheel [0...360] + + Returns: + An approximation of the corresponding hue on Itten's RYB wheel. + + >>> rgb_to_ryb(15) + 26.0 + + """ + d = hue % 15 + i = int(hue / 15) + x0 = _RybWheel[i] + x1 = _RybWheel[i+1] + return x0 + (x1-x0) * d / 15 + +def ryb_to_rgb(hue): + """Maps a hue on Itten's RYB color wheel to the standard RGB wheel. + + Parameters: + :hue: + The hue on Itten's RYB color wheel [0...360] + + Returns: + An approximation of the corresponding hue on the standard RGB wheel. + + >>> ryb_to_rgb(15) + 8.0 + + """ + d = hue % 15 + i = int(hue / 15) + x0 = _RgbWheel[i] + x1 = _RgbWheel[i+1] + return x0 + (x1-x0) * d / 15 + + +class Color(object): + """Hold a color value. + + Example usage: + + To create an instance of the grapefruit.Color from RGB values: + + >>> import grapefruit + >>> r, g, b = 1, 0.5, 0 + >>> col = grapefruit.Color.from_rgb(r, g, b) + + To get the values of the color in another colorspace: + + >>> h, s, v = col.hsv + >>> l, a, b = col.lab + + To get the complementary of a color: + + >>> compl = col.complementary_color(mode='rgb') + >>> compl.hsl + (210.0, 1.0, 0.5) + + To directly convert RGB values to their HSL equivalent: + + >>> h, s, l = rgb_to_hsl(r, g, b) + + """ + + # --==================-------------------------------------------------------- + # -- Creation methods -- + # --==================-- + + @staticmethod + def from_rgb(r, g, b, alpha=1.0, wref=_DEFAULT_WREF): + """Create a new instance based on the specifed RGB values. + + Parameters: + :r: + The Red component value [0...1] + :g: + The Green component value [0...1] + :b: + The Blue component value [0...1] + :alpha: + The color transparency [0...1], default is opaque + :wref: + The whitepoint reference, default is 2° D65. + + Returns: + A grapefruit.Color instance. + + >>> Color.from_rgb(1.0, 0.5, 0.0) + Color(1.0, 0.5, 0.0, 1.0) + >>> Color.from_rgb(1.0, 0.5, 0.0, 0.5) + Color(1.0, 0.5, 0.0, 0.5) + + """ + return Color((r, g, b), 'rgb', alpha, wref) + + @staticmethod + def from_hsl(h, s, l, alpha=1.0, wref=_DEFAULT_WREF): + """Create a new instance based on the specifed HSL values. + + Parameters: + :h: + The Hue component value [0...1] + :s: + The Saturation component value [0...1] + :l: + The Lightness component value [0...1] + :alpha: + The color transparency [0...1], default is opaque + :wref: + The whitepoint reference, default is 2° D65. + + Returns: + A grapefruit.Color instance. + + >>> Color.from_hsl(30, 1, 0.5) + Color(1.0, 0.5, 0.0, 1.0) + >>> Color.from_hsl(30, 1, 0.5, 0.5) + Color(1.0, 0.5, 0.0, 0.5) + + """ + return Color((h, s, l), 'hsl', alpha, wref) + + @staticmethod + def from_hsv(h, s, v, alpha=1.0, wref=_DEFAULT_WREF): + """Create a new instance based on the specifed HSV values. + + Parameters: + :h: + The Hus component value [0...1] + :s: + The Saturation component value [0...1] + :v: + The Value component [0...1] + :alpha: + The color transparency [0...1], default is opaque + :wref: + The whitepoint reference, default is 2° D65. + + Returns: + A grapefruit.Color instance. + + >>> Color.from_hsv(30, 1, 1) + Color(1.0, 0.5, 0.0, 1.0) + >>> Color.from_hsv(30, 1, 1, 0.5) + Color(1.0, 0.5, 0.0, 0.5) + + """ + h2, s, l = rgb_to_hsl(*hsv_to_rgb(h, s, v)) + return Color((h, s, l), 'hsl', alpha, wref) + + @staticmethod + def from_yiq(y, i, q, alpha=1.0, wref=_DEFAULT_WREF): + """Create a new instance based on the specifed YIQ values. + + Parameters: + :y: + The Y component value [0...1] + :i: + The I component value [0...1] + :q: + The Q component value [0...1] + :alpha: + The color transparency [0...1], default is opaque + :wref: + The whitepoint reference, default is 2° D65. + + Returns: + A grapefruit.Color instance. + + >>> Color.from_yiq(0.5922, 0.45885,-0.05) + Color(0.999902, 0.499955, -6.7e-05, 1.0) + >>> Color.from_yiq(0.5922, 0.45885,-0.05, 0.5) + Color(0.999902, 0.499955, -6.7e-05, 0.5) + + """ + return Color(yiq_to_rgb(y, i, q), 'rgb', alpha, wref) + + @staticmethod + def from_yuv(y, u, v, alpha=1.0, wref=_DEFAULT_WREF): + """Create a new instance based on the specifed YUV values. + + Parameters: + :y: + The Y component value [0...1] + :u: + The U component value [-0.436...0.436] + :v: + The V component value [-0.615...0.615] + :alpha: + The color transparency [0...1], default is opaque + :wref: + The whitepoint reference, default is 2° D65. + + Returns: + A grapefruit.Color instance. + + >>> Color.from_yuv(0.5925, -0.2916, 0.3575) + Color(0.999989, 0.500015, -6.3e-05, 1.0) + >>> Color.from_yuv(0.5925, -0.2916, 0.3575, 0.5) + Color(0.999989, 0.500015, -6.3e-05, 0.5) + + """ + return Color(yuv_to_rgb(y, u, v), 'rgb', alpha, wref) + + @staticmethod + def from_xyz(x, y, z, alpha=1.0, wref=_DEFAULT_WREF): + """Create a new instance based on the specifed CIE-XYZ values. + + Parameters: + :x: + The Red component value [0...1] + :y: + The Green component value [0...1] + :z: + The Blue component value [0...1] + :alpha: + The color transparency [0...1], default is opaque + :wref: + The whitepoint reference, default is 2° D65. + + Returns: + A grapefruit.Color instance. + + >>> Color.from_xyz(0.488941, 0.365682, 0.0448137) + Color(1.0, 0.5, 0.0, 1.0) + >>> Color.from_xyz(0.488941, 0.365682, 0.0448137, 0.5) + Color(1.0, 0.5, 0.0, 0.5) + + """ + return Color(xyz_to_rgb(x, y, z), 'rgb', alpha, wref) + + @staticmethod + def from_lab(l, a, b, alpha=1.0, wref=_DEFAULT_WREF): + """Create a new instance based on the specifed CIE-LAB values. + + Parameters: + :l: + The L component [0...100] + :a: + The a component [-1...1] + :b: + The a component [-1...1] + :alpha: + The color transparency [0...1], default is opaque + :wref: + The whitepoint reference, default is 2° D65. + + Returns: + A grapefruit.Color instance. + + >>> Color.from_lab(66.951823, 0.43084105, 0.73969231) + Color(1.0, 0.5, -0.0, 1.0) + >>> Color.from_lab(66.951823, 0.41165967, 0.67282012, wref=WHITE_REFERENCE['std_D50']) + Color(1.0, 0.5, -0.0, 1.0) + >>> Color.from_lab(66.951823, 0.43084105, 0.73969231, 0.5) + Color(1.0, 0.5, -0.0, 0.5) + >>> Color.from_lab(66.951823, 0.41165967, 0.67282012, 0.5, WHITE_REFERENCE['std_D50']) + Color(1.0, 0.5, -0.0, 0.5) + + """ + return Color(xyz_to_rgb(*lab_to_xyz(l, a, b, wref)), 'rgb', alpha, wref) + + @staticmethod + def from_cmy(c, m, y, alpha=1.0, wref=_DEFAULT_WREF): + """Create a new instance based on the specifed CMY values. + + Parameters: + :c: + The Cyan component value [0...1] + :m: + The Magenta component value [0...1] + :y: + The Yellow component value [0...1] + :alpha: + The color transparency [0...1], default is opaque + :wref: + The whitepoint reference, default is 2° D65. + + Returns: + A grapefruit.Color instance. + + >>> Color.from_cmy(0, 0.5, 1) + Color(1.0, 0.5, 0.0, 1.0) + >>> Color.from_cmy(0, 0.5, 1, 0.5) + Color(1.0, 0.5, 0.0, 0.5) + + """ + return Color(cmy_to_rgb(c, m, y), 'rgb', alpha, wref) + + @staticmethod + def from_cmyk(c, m, y, k, alpha=1.0, wref=_DEFAULT_WREF): + """Create a new instance based on the specifed CMYK values. + + Parameters: + :c: + The Cyan component value [0...1] + :m: + The Magenta component value [0...1] + :y: + The Yellow component value [0...1] + :k: + The Black component value [0...1] + :alpha: + The color transparency [0...1], default is opaque + :wref: + The whitepoint reference, default is 2° D65. + + Returns: + A grapefruit.Color instance. + + >>> Color.from_cmyk(1, 0.32, 0, 0.5) + Color(0.0, 0.34, 0.5, 1.0) + >>> Color.from_cmyk(1, 0.32, 0, 0.5, 0.5) + Color(0.0, 0.34, 0.5, 0.5) + + """ + return Color(cmy_to_rgb(*cmyk_to_cmy(c, m, y, k)), 'rgb', alpha, wref) + + @staticmethod + def from_html(html, alpha=1.0, wref=_DEFAULT_WREF): + """Create a new instance based on the specifed HTML color definition. + + Parameters: + :html: + The HTML definition of the color (#RRGGBB or #RGB or a color name). + :alpha: + The color transparency [0...1], default is opaque. + :wref: + The whitepoint reference, default is 2° D65. + + Returns: + A grapefruit.Color instance. + + >>> Color.from_html('#ff8000') + Color(1.0, 0.501961, 0.0, 1.0) + >>> Color.from_html('ff8000') + Color(1.0, 0.501961, 0.0, 1.0) + >>> Color.from_html('#f60') + Color(1.0, 0.4, 0.0, 1.0) + >>> Color.from_html('f60') + Color(1.0, 0.4, 0.0, 1.0) + >>> Color.from_html('lemonchiffon') + Color(1.0, 0.980392, 0.803922, 1.0) + >>> Color.from_html('#ff8000', 0.5) + Color(1.0, 0.501961, 0.0, 0.5) + + """ + return Color(html_to_rgb(html), 'rgb', alpha, wref) + + @staticmethod + def from_pil(pil, alpha=1.0, wref=_DEFAULT_WREF): + """Create a new instance based on the specifed PIL color. + + Parameters: + :pil: + A PIL compatible color representation (0xBBGGRR) + :alpha: + The color transparency [0...1], default is opaque + :wref: + The whitepoint reference, default is 2° D65. + + Returns: + A grapefruit.Color instance. + + >>> Color.from_pil(0x0080ff) + Color(1.0, 0.501961, 0.0, 1.0) + >>> Color.from_pil(0x0080ff, 0.5) + Color(1.0, 0.501961, 0.0, 0.5) + + """ + return Color(pil_to_rgb(pil), 'rgb', alpha, wref) + + + def __init__(self, values, mode='rgb', alpha=1.0, wref=_DEFAULT_WREF): + """Instantiate a new grapefruit.Color object. + + Parameters: + :values: + The values of this color, in the specified representation. + :mode: + The representation mode used for values. + :alpha: + the alpha value (transparency) of this color. + :wref: + The whitepoint reference, default is 2° D65. + + """ + if not(isinstance(values, tuple)): + raise TypeError("values must be a tuple") + + if mode=='rgb': + self.__rgb = tuple([float(v) for v in values]) + self.__hsl = rgb_to_hsl(*self.__rgb) + elif mode=='hsl': + self.__hsl = tuple([float(v) for v in values]) + self.__rgb = hsl_to_rgb(*self.__hsl) + else: + raise ValueError("Invalid color mode: " + mode) + + self.__a = alpha + self.__wref = wref + + # --=====================----------------------------------------------------- + # -- Convenience methods -- + # --=====================-- + + def __ne__(self, other): + return not self.__eq__(other) + + def __eq__(self, other): + try: + if isinstance(other, Color): + return (self.__rgb==other.__rgb) and (self.__a==other.__a) and (self.__wref==other.__wref) + if len(other) != 4: + return False + return list(self.__rgb + (self.__a,)) == list(other) + except TypeError: + return False + except AttributeError: + return False + + def __repr__(self): + return "Color({}, {}, {}, {})".format(*[round(v, 6) for v in (self.__rgb + (self.__a,))]) + + def __str__(self): + """A string representation of this grapefruit.Color instance. + + Returns: + The RGBA representation of this grapefruit.Color instance. + + """ + return "({}, {}, {}, {})".format(*[round(v, 6) for v in (self.__rgb + (self.__a,))]) + + if sys.version_info[0] < 3: + def __unicode__(self): + """A unicode string representation of this grapefruit.Color instance. + + Returns: + The RGBA representation of this grapefruit.Color instance. + + """ + return str("({}, {}, {}, {})".format(*[round(v, 6) for v in (self.__rgb + (self.__a,))])) + + def __iter__(self): + return iter(self.__rgb + (self.__a,)) + + def __len__(self): + return 4 + + # --============-------------------------------------------------------------- + # -- Properties -- + # --============-- + + @property + def is_legal(self): + """Boolean indicating whether the color is within the legal gamut.""" + return all(0.0 <= v <= 1.0 for v in self) + + @property + def alpha(self): + """The transparency of this color. 0.0 is transparent and 1.0 is fully opaque.""" + return self.__a + @alpha.setter + def alpha(self, value): + self.__a = value + + @property + def white_ref(self): + """the white reference point of this color.""" + return self.__wref + @white_ref.setter + def white_ref(self, value): + self.__wref = value + + + @property + def rgb(self): + """The RGB values of this Color.""" + return self.__rgb + @rgb.setter + def rgb(self, value): + self.__rgb = tuple([float(v) for v in value]) + self.__hsl = rgb_to_hsl(*self.__rgb) + + @property + def rgba(self): + """The RGBA values of this Color.""" + return (self.__rgb + (self.__a,)) + @rgba.setter + def rgba(self, value): + self.__rgb = tuple([float(v) for v in value[:3]]) + self.__a = float(value[3]) + self.__hsl = rgb_to_hsl(*self.__rgb) + + @property + def red(self): + return self.__rgb[0] + @red.setter + def red(self, value): + self.__rgb = (float(value), self.__rgb[1], self.__rgb[2]) + self.__hsl = rgb_to_hsl(*self.__rgb) + + @property + def green(self): + return self.__rgb[1] + @green.setter + def green(self, value): + self.__rgb = (self.__rgb[0], float(value), self.__rgb[2]) + self.__hsl = rgb_to_hsl(*self.__rgb) + + @property + def blue(self): + return self.__rgb[2] + @blue.setter + def blue(self, value): + self.__rgb = (self.__rgb[0], self.__rgb[1], float(value)) + self.__hsl = rgb_to_hsl(*self.__rgb) + + + @property + def hsl(self): + """The HSL values of this Color.""" + return self.__hsl + @hsl.setter + def hsl(self, value): + self.__hsl = tuple([float(v) for v in value]) + self.__rgb = hsl_to_rgb(*self.__hsl) + + @property + def hsl_hue(self): + """The hue of this color.""" + return self.__hsl[0] + @hsl_hue.setter + def hsl_hue(self, value): + self.__hsl = (float(value), self.__hsl[1], self.__hsl[2]) + self.__rgb = hsl_to_rgb(*self.__hsl) + + @property + def hsv(self): + """The HSV values of this Color.""" + return rgb_to_hsv(*self.__rgb) + @hsv.setter + def hsv(self, value): + self.__rgb = hsv_to_rgb(*value) + self.__hsl = rgb_to_hsl(*self.__rgb) + + @property + def yiq(self): + """The YIQ values of this Color.""" + return rgb_to_yiq(*self.__rgb) + @yiq.setter + def yiq(self, value): + self.__rgb = yiq_to_rgb(*value) + self.__hsl = rgb_to_hsl(*self.__rgb) + + @property + def yuv(self): + """The YUV values of this Color.""" + return rgb_to_yuv(*self.__rgb) + @yuv.setter + def yuv(self, value): + self.__rgb = yuv_to_rgb(*value) + self.__hsl = rgb_to_hsl(*self.__rgb) + + @property + def xyz(self): + """The CIE-XYZ values of this Color.""" + return rgb_to_xyz(*self.__rgb) + @xyz.setter + def xyz(self, value): + self.__rgb = xyz_to_rgb(*value) + self.__hsl = rgb_to_hsl(*self.__rgb) + + @property + def lab(self): + """The CIE-LAB values of this Color.""" + return xyz_to_lab(wref=self.__wref, *rgb_to_xyz(*self.__rgb)) + @lab.setter + def lab(self, value): + self.__rgb = xyz_to_rgb(lab_to_xyz(*value)) + self.__hsl = rgb_to_hsl(*self.__rgb) + + @property + def cmy(self): + """The CMY values of this Color.""" + return rgb_to_cmy(*self.__rgb) + @cmy.setter + def cmy(self, value): + self.__rgb = cmy_to_rgb(*value) + self.__hsl = rgb_to_hsl(*self.__rgb) + + @property + def cmyk(self): + """The CMYK values of this Color.""" + return cmy_to_cmyk(*rgb_to_cmy(*self.__rgb)) + @cmyk.setter + def cmyk(self, value): + self.__rgb = cmy_to_rgb(*cmyk_to_cmy(*value)) + self.__hsl = rgb_to_hsl(*self.__rgb) + + @property + def ints(self): + """This Color as a tuple of integers in the range [0...255]""" + return rgb_to_ints(*self.__rgb) + @ints.setter + def ints(self, value): + self.__rgb = ints_to_rgb(*value) + self.__hsl = rgb_to_hsl(*self.__rgb) + + @property + def html(self): + """This Color as an HTML color definition.""" + return rgb_to_html(*self.__rgb) + @html.setter + def html(self, value): + self.__rgb = html_to_rgb(value) + self.__hsl = rgb_to_hsl(*self.__rgb) + + @property + def pil(self): + """This Color as a PIL compatible value.""" + return rgb_to_pil(*self.__rgb) + @pil.setter + def pil(self, value): + self.__rgb = pil_to_rgb(value) + self.__hsl = rgb_to_hsl(*self.__rgb) + + @property + def websafe(self): + """The web safe color nearest to this one (RGB).""" + return rgb_to_websafe(*self.__rgb) + + @property + def greyscale(self): + """The greyscale equivalent to this color (RGB).""" + return rgb_to_greyscale(*self.rgb) + + def with_alpha(self, alpha): + """Create a new instance based on this one with a new alpha value. + + Parameters: + :alpha: + The transparency of the new color [0...1]. + + Returns: + A grapefruit.Color instance. + + >>> Color.from_rgb(1.0, 0.5, 0.0, 1.0).with_alpha(0.5) + Color(1.0, 0.5, 0.0, 0.5) + + """ + return Color(self.__rgb, 'rgb', alpha, self.__wref) + + def with_white_ref(self, wref, labAsRef=False): + """Create a new instance based on this one with a new white reference. + + Parameters: + :wref: + The whitepoint reference. + :labAsRef: + If True, the L*a*b* values of the current instance are used as reference + for the new color; otherwise, the RGB values are used as reference. + + Returns: + A grapefruit.Color instance. + + + >>> c = Color.from_rgb(1.0, 0.5, 0.0, 1.0, WHITE_REFERENCE['std_D65']) + + >>> c2 = c.with_white_ref(WHITE_REFERENCE['sup_D50']) + >>> c2.rgb + (1.0, 0.5, 0.0) + >>> '(%g, %g, %g)' % c2.white_ref + '(0.967206, 1, 0.81428)' + + >>> c2 = c.with_white_ref(WHITE_REFERENCE['sup_D50'], labAsRef=True) + >>> '(%g, %g, %g)' % c2.rgb + '(1.01463, 0.490341, -0.148133)' + >>> '(%g, %g, %g)' % c2.white_ref + '(0.967206, 1, 0.81428)' + >>> '(%g, %g, %g)' % c.lab + '(66.9518, 0.430841, 0.739692)' + >>> '(%g, %g, %g)' % c2.lab + '(66.9518, 0.430841, 0.739693)' + + """ + if labAsRef: + l, a, b = self.lab + return Color.from_lab(l, a, b, self.__a, wref) + else: + return Color(self.__rgb, 'rgb', self.__a, wref) + + def with_hue(self, hue): + """Create a new instance based on this one with a new hue. + + Parameters: + :hue: + The hue of the new color [0...360]. + + Returns: + A grapefruit.Color instance. + + >>> Color.from_hsl(30, 1, 0.5).with_hue(60) + Color(1.0, 1.0, 0.0, 1.0) + >>> Color.from_hsl(30, 1, 0.5).with_hue(60).hsl + (60.0, 1.0, 0.5) + + """ + h, s, l = self.__hsl + return Color((hue, s, l), 'hsl', self.__a, self.__wref) + + def with_saturation(self, saturation): + """Create a new instance based on this one with a new saturation value. + + .. note:: + + The saturation is defined for the HSL mode. + + Parameters: + :saturation: + The saturation of the new color [0...1]. + + Returns: + A grapefruit.Color instance. + + >>> Color.from_hsl(30, 1, 0.5).with_saturation(0.5) + Color(0.75, 0.5, 0.25, 1.0) + >>> Color.from_hsl(30, 1, 0.5).with_saturation(0.5).hsl + (30.0, 0.5, 0.5) + + """ + h, s, l = self.__hsl + return Color((h, saturation, l), 'hsl', self.__a, self.__wref) + + def with_lightness(self, lightness): + """Create a new instance based on this one with a new lightness value. + + Parameters: + :lightness: + The lightness of the new color [0...1]. + + Returns: + A grapefruit.Color instance. + + >>> Color.from_hsl(30, 1, 0.5).with_lightness(0.25) + Color(0.5, 0.25, 0.0, 1.0) + >>> Color.from_hsl(30, 1, 0.5).with_lightness(0.25).hsl + (30.0, 1.0, 0.25) + + """ + h, s, l = self.__hsl + return Color((h, s, lightness), 'hsl', self.__a, self.__wref) + + def darker(self, level): + """Create a new instance based on this one but darker. + + Parameters: + :level: + The amount by which the color should be darkened to produce + the new one [0...1]. + + Returns: + A grapefruit.Color instance. + + >>> Color.from_hsl(30, 1, 0.5).darker(0.25) + Color(0.5, 0.25, 0.0, 1.0) + >>> Color.from_hsl(30, 1, 0.5).darker(0.25).hsl + (30.0, 1.0, 0.25) + + """ + h, s, l = self.__hsl + return Color((h, s, max(l - level, 0)), 'hsl', self.__a, self.__wref) + + def lighter(self, level): + """Create a new instance based on this one but lighter. + + Parameters: + :level: + The amount by which the color should be lightened to produce + the new one [0...1]. + + Returns: + A grapefruit.Color instance. + + >>> Color.from_hsl(30, 1, 0.5).lighter(0.25) + Color(1.0, 0.75, 0.5, 1.0) + >>> Color.from_hsl(30, 1, 0.5).lighter(0.25).hsl + (30.0, 1.0, 0.75) + + """ + h, s, l = self.__hsl + return Color((h, s, min(l + level, 1)), 'hsl', self.__a, self.__wref) + + def saturate(self, level): + """Create a new instance based on this one but more saturated. + + Parameters: + :level: + The amount by which the color should be saturated to produce + the new one [0...1]. + + Returns: + A grapefruit.Color instance. + + >>> Color.from_hsl(30, 0.5, 0.5).saturate(0.25) + Color(0.875, 0.5, 0.125, 1.0) + >>> Color.from_hsl(30, 0.5, 0.5).saturate(0.25).hsl + (30.0, 0.75, 0.5) + + """ + h, s, l = self.__hsl + return Color((h, min(s + level, 1), l), 'hsl', self.__a, self.__wref) + + def desaturate(self, level): + """Create a new instance based on this one but less saturated. + + Parameters: + :level: + The amount by which the color should be desaturated to produce + the new one [0...1]. + + Returns: + A grapefruit.Color instance. + + >>> Color.from_hsl(30, 0.5, 0.5).desaturate(0.25) + Color(0.625, 0.5, 0.375, 1.0) + >>> Color.from_hsl(30, 0.5, 0.5).desaturate(0.25).hsl + (30.0, 0.25, 0.5) + + """ + h, s, l = self.__hsl + return Color((h, max(s - level, 0), l), 'hsl', self.__a, self.__wref) + + def nearest_legal(self): + """Create a new instance that is the nearest legal color to this one. + + >>> Color.from_rgb(2.0, 0.0, 3.0).nearest_legal() + Color(1.0, 0.0, 1.0, 1.0) + """ + return Color.from_rgb(*[max(min(v, 1.0), 0.0) for v in self]) + + def websafe_dither(self): + """Return the two websafe colors nearest to this one. + + Returns: + A tuple of two grapefruit.Color instances which are the two + web safe colors closest this one. + + >>> c = Color.from_rgb(1.0, 0.45, 0.0) + >>> c1, c2 = c.websafe_dither() + >>> c1 + Color(1.0, 0.4, 0.0, 1.0) + >>> c2 + Color(1.0, 0.6, 0.0, 1.0) + + """ + return ( + Color(rgb_to_websafe(*self.__rgb), 'rgb', self.__a, self.__wref), + Color(rgb_to_websafe(alt=True, *self.__rgb), 'rgb', self.__a, self.__wref)) + + def complementary_color(self, mode='ryb'): + """Create a new instance which is the complementary color of this one. + + Parameters: + :mode: + Select which color wheel to use for the generation (ryb/rgb). + + + Returns: + A grapefruit.Color instance. + + >>> Color.from_hsl(30, 1, 0.5).complementary_color(mode='rgb') + Color(0.0, 0.5, 1.0, 1.0) + >>> Color.from_hsl(30, 1, 0.5).complementary_color(mode='rgb').hsl + (210.0, 1.0, 0.5) + + """ + h, s, l = self.__hsl + + if mode == 'ryb': h = rgb_to_ryb(h) + h = (h+180)%360 + if mode == 'ryb': h = ryb_to_rgb(h) + + return Color((h, s, l), 'hsl', self.__a, self.__wref) + + def make_gradient(self, target, steps=100): + """Create a list with the gradient colors between this and the other color. + + Parameters: + :target: + The grapefruit.Color at the other end of the gradient. + :steps: + The number of gradients steps to create. + + + Returns: + A list of grapefruit.Color instances. + + >>> c1 = Color.from_rgb(1.0, 0.0, 0.0, alpha=1) + >>> c2 = Color.from_rgb(0.0, 1.0, 0.0, alpha=0) + >>> c1.make_gradient(c2, 3) + [Color(0.75, 0.25, 0.0, 0.75), Color(0.5, 0.5, 0.0, 0.5), Color(0.25, 0.75, 0.0, 0.25)] + + """ + gradient = [] + rgba1 = self.__rgb + (self.__a,) + rgba2 = target.__rgb + (target.__a,) + + steps += 1 + for n in range(1, steps): + d = 1.0*n/steps + r = (rgba1[0]*(1-d)) + (rgba2[0]*d) + g = (rgba1[1]*(1-d)) + (rgba2[1]*d) + b = (rgba1[2]*(1-d)) + (rgba2[2]*d) + a = (rgba1[3]*(1-d)) + (rgba2[3]*d) + + gradient.append(Color((r, g, b), 'rgb', a, self.__wref)) + + return gradient + + def make_monochrome_scheme(self): + """Return 4 colors in the same hue with varying saturation/lightness. + + Returns: + A tuple of 4 grapefruit.Color in the same hue as this one, + with varying saturation/lightness. + + >>> c = Color.from_hsl(30, 0.5, 0.5) + >>> ['(%g, %g, %g)' % clr.hsl for clr in c.make_monochrome_scheme()] + ['(30, 0.2, 0.8)', '(30, 0.5, 0.3)', '(30, 0.2, 0.6)', '(30, 0.5, 0.8)'] + + """ + def _wrap(x, min, thres, plus): + if (x-min) < thres: return x + plus + else: return x-min + + h, s, l = self.__hsl + + s1 = _wrap(s, 0.3, 0.1, 0.3) + l1 = _wrap(l, 0.5, 0.2, 0.3) + + s2 = s + l2 = _wrap(l, 0.2, 0.2, 0.6) + + s3 = s1 + l3 = max(0.2, l + (1-l)*0.2) + + s4 = s + l4 = _wrap(l, 0.5, 0.2, 0.3) + + return ( + Color((h, s1, l1), 'hsl', self.__a, self.__wref), + Color((h, s2, l2), 'hsl', self.__a, self.__wref), + Color((h, s3, l3), 'hsl', self.__a, self.__wref), + Color((h, s4, l4), 'hsl', self.__a, self.__wref)) + + def make_triadic_scheme(self, angle=120, mode='ryb'): + """Return two colors forming a triad or a split complementary with this one. + + Parameters: + :angle: + The angle between the hues of the created colors. + The default value makes a triad. + :mode: + Select which color wheel to use for the generation (ryb/rgb). + + Returns: + A tuple of two grapefruit.Color forming a color triad with + this one or a split complementary. + + >>> c1 = Color.from_hsl(30, 1, 0.5) + + >>> c2, c3 = c1.make_triadic_scheme(mode='rgb') + >>> c2.hsl + (150.0, 1.0, 0.5) + >>> c3.hsl + (270.0, 1.0, 0.5) + + >>> c2, c3 = c1.make_triadic_scheme(angle=40, mode='rgb') + >>> c2.hsl + (190.0, 1.0, 0.5) + >>> c3.hsl + (230.0, 1.0, 0.5) + + """ + h, s, l = self.__hsl + angle = min(angle, 120) / 2.0 + + if mode == 'ryb': h = rgb_to_ryb(h) + h += 180 + h1 = (h - angle) % 360 + h2 = (h + angle) % 360 + if mode == 'ryb': + h1 = ryb_to_rgb(h1) + h2 = ryb_to_rgb(h2) + + return ( + Color((h1, s, l), 'hsl', self.__a, self.__wref), + Color((h2, s, l), 'hsl', self.__a, self.__wref)) + + def make_tetradic_scheme(self, angle=30, mode='ryb'): + """Return three colors froming a tetrad with this one. + + Parameters: + :angle: + The angle to substract from the adjacent colors hues [-90...90]. + You can use an angle of zero to generate a square tetrad. + :mode: + Select which color wheel to use for the generation (ryb/rgb). + + Returns: + A tuple of three grapefruit.Color forming a color tetrad with + this one. + + >>> col = Color.from_hsl(30, 1, 0.5) + >>> [c.hsl for c in col.make_tetradic_scheme(mode='rgb', angle=30)] + [(90.0, 1.0, 0.5), (210.0, 1.0, 0.5), (270.0, 1.0, 0.5)] + + """ + h, s, l = self.__hsl + + if mode == 'ryb': h = rgb_to_ryb(h) + h1 = (h + 90 - angle) % 360 + h2 = (h + 180) % 360 + h3 = (h + 270 - angle) % 360 + if mode == 'ryb': + h1 = ryb_to_rgb(h1) + h2 = ryb_to_rgb(h2) + h3 = ryb_to_rgb(h3) + + return ( + Color((h1, s, l), 'hsl', self.__a, self.__wref), + Color((h2, s, l), 'hsl', self.__a, self.__wref), + Color((h3, s, l), 'hsl', self.__a, self.__wref)) + + def make_analogous_scheme(self, angle=30, mode='ryb'): + """Return two colors analogous to this one. + + Args: + :angle: + The angle between the hues of the created colors and this one. + :mode: + Select which color wheel to use for the generation (ryb/rgb). + + Returns: + A tuple of grapefruit.Colors analogous to this one. + + >>> c1 = Color.from_hsl(30, 1, 0.5) + + >>> c2, c3 = c1.make_analogous_scheme(angle=60, mode='rgb') + >>> c2.hsl + (330.0, 1.0, 0.5) + >>> c3.hsl + (90.0, 1.0, 0.5) + + >>> c2, c3 = c1.make_analogous_scheme(angle=10, mode='rgb') + >>> c2.hsl + (20.0, 1.0, 0.5) + >>> c3.hsl + (40.0, 1.0, 0.5) + + """ + h, s, l = self.__hsl + + if mode == 'ryb': h = rgb_to_ryb(h) + h += 360 + h1 = (h - angle) % 360 + h2 = (h + angle) % 360 + if mode == 'ryb': + h1 = ryb_to_rgb(h1) + h2 = ryb_to_rgb(h2) + + return (Color((h1, s, l), 'hsl', self.__a, self.__wref), + Color((h2, s, l), 'hsl', self.__a, self.__wref)) + + def alpha_blend(self, other): + """Alpha-blend this color on the other one. + + Args: + :other: + The grapefruit.Color to alpha-blend with this one. + + Returns: + A grapefruit.Color instance which is the result of alpha-blending + this color on the other one. + + >>> c1 = Color.from_rgb(1, 0.5, 0, 0.2) + >>> c2 = Color.from_rgb(1, 1, 1, 0.8) + >>> c3 = c1.alpha_blend(c2) + >>> c3 + Color(1.0, 0.875, 0.75, 0.84) + + """ + # get final alpha channel + fa = self.__a + other.__a - (self.__a * other.__a) + + # get percentage of source alpha compared to final alpha + if fa==0: sa = 0 + else: sa = min(1.0, self.__a/other.__a) + + # destination percentage is just the additive inverse + da = 1.0 - sa + + sr, sg, sb = [v * sa for v in self.__rgb] + dr, dg, db = [v * da for v in other.__rgb] + + return Color((sr+dr, sg+dg, sb+db), 'rgb', fa, self.__wref) + + def blend(self, other, percent=0.5): + """blend this color with the other one. + + Args: + :other: + the grapefruit.Color to blend with this one. + + Returns: + A grapefruit.Color instance which is the result of blending + this color on the other one. + + >>> c1 = Color.from_rgb(1, 0.5, 0, 0.2) + >>> c2 = Color.from_rgb(1, 1, 1, 0.6) + >>> c3 = c1.blend(c2) + >>> c3 + Color(1.0, 0.75, 0.5, 0.4) + + """ + dest = 1.0 - percent + rgb = tuple(((u * percent) + (v * dest) for u, v in zip(self.__rgb, other.__rgb))) + a = (self.__a * percent) + (other.__a * dest) + return Color(rgb, 'rgb', a, self.__wref) + +def _test(): + import doctest + reload(doctest) + doctest.testmod() + +if __name__=='__main__': + _test() + +# vim: ts=2 sts=2 sw=2 et diff --git a/dist/grapefruit-0.1a4-py3.6.egg b/dist/grapefruit-0.1a4-py3.6.egg new file mode 100644 index 0000000000000000000000000000000000000000..92a4e91e503ed6c1943c2511e9be2be7650a31e0 GIT binary patch literal 32968 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 v2=Tx4