spectra-0.0.11/0000755000076500000240000000000013231775613016027 5ustar jeremysinger-vinestaff00000000000000spectra-0.0.11/LICENSE.txt0000644000076500000240000000207612503555656017663 0ustar jeremysinger-vinestaff00000000000000The MIT License (MIT) Copyright (c) 2015, Jeremy Singer-Vine Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. spectra-0.0.11/MANIFEST.in0000644000076500000240000000007713111677523017567 0ustar jeremysinger-vinestaff00000000000000include LICENSE.txt include README.md include requirements.txt spectra-0.0.11/PKG-INFO0000644000076500000240000000136413231775613017130 0ustar jeremysinger-vinestaff00000000000000Metadata-Version: 1.1 Name: spectra Version: 0.0.11 Summary: Color scales and color conversion made easy for Python. Home-page: http://github.com/jsvine/spectra Author: Jeremy Singer-Vine Author-email: jsvine@gmail.com License: MIT Description: UNKNOWN Keywords: color colors colorspace scale spectrum Platform: UNKNOWN Classifier: Development Status :: 3 - Alpha Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3.1 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 spectra-0.0.11/README.md0000644000076500000240000001202412551724551017304 0ustar jeremysinger-vinestaff00000000000000# Spectra Spectra is a Python library that makes color math, color scales, and color-space conversion easy. Support for: - Color scales - Color ranges - Color blending - Brightening/darkening colors - Saturating/desaturating colors - Conversion to/from multiple [color spaces](http://en.wikipedia.org/wiki/Color_space) Spectra is built on [colormath](http://python-colormath.readthedocs.org/) and [grapefruit](https://github.com/xav/Grapefruit). Spectra is *enormously* inspired by [chroma.js](https://github.com/gka/chroma.js) and [d3's scales](https://github.com/mbostock/d3/wiki/Quantitative-Scales). ## Installation ```sh pip install spectra ``` ## Walkthrough See [this walkthrough](docs/walkthrough.ipynb) to see what Spectra can do. ## API ### Creating color objects from web-colors or hexcode strings ##### `spectra.html(html_string)` E.g., `spectra.html("papayawhip")`, `spectra.html("#BAABAA")` `spectra.html("#FFF")` ### Creating color objects from color space values ##### `spectra.rgb(r, g, b)` Specifically: [sRGB](http://en.wikipedia.org/wiki/SRGB) --- ##### `spectra.lab(L, a, b)` Specifically: [CIELAB](http://en.wikipedia.org/wiki/Lab_color_space#CIELAB) --- ##### `spectra.lch(L, c, h)` Also known elsewhere as "hcl" --- ##### `spectra.hsl(h, s, l)` --- ##### `spectra.hsv(h, s, v)` --- ##### `spectra.xyz(x, y, z)` --- ##### `spectra.cmy(c, m, y)` --- ##### `spectra.cmky(c, m, y, k)` --- ### Getting color values Instances of `spectra.Color` have four main properties: - __`.values`__: An array representation of the color's values in its own color space, e.g. `(L, a, b)` for an `lab` color. - __`.hexcode`__: The hex encoding of this color, e.g. `#ffffff` for `rgb(255, 255, 255)`/`html(\"white\")`. - __`.rgb`__: The `(r, g, b)` values for this color in the `rgb` color space; these are allowed to go out of gamut. - __`.clamped_rgb`__: The \"clamped\" `(r, g, b)` values for this color in the `rgb` color space. Note on `.rgb` and `.rgb_clamped`: Spectra follows [colormath](http://python-colormath.readthedocs.org/en/latest/conversions.html?highlight=clamp#rgb-conversions-and-out-of-gamut-coordinates)'s convention: > RGB spaces tend to have a smaller gamut than some of the CIE color spaces. When converting to RGB, this can cause some of the coordinates to end up being out of the acceptable range (0.0-1.0 or 1-255, depending on whether your RGB color is upscaled). [...] Rather than clamp these for you, we leave them as-is. ### Modifying colors ##### `color.to(space)` Convert this color to another color space. ```python teal_lab = spectra.html("teal").to("lab") print(teal_lab.values) >>> (48.25453959565715, -28.843707890081394, -8.48135382506432) ``` --- ##### `color.blend(other_color, ratio=0.5)` Blend this color with another color, using `ratio` of that other color. ```python yellow, red = spectra.html("red"), spectra.html("yellow") orange = yellow.blend(red) print(orange.hexcode) >>> '#ff8000' ``` --- ##### `color.brighten(amount=10)` Brighten this color by `amount` luminance. (Converts this color to the LCH color space, and then increases the `L` parameter by `amount`.) ```python teal = spectra.html("teal") light_teal = light_teal.brighten(30) print(light_teal.hexcode) >>> '#75d1d0' ``` --- ##### `color.darken(amount=10)` The opposite of `color.brighten`; *reduces* color by `amount` luminance. --- ##### `color.saturate(amount=10)` Saturate this color by `amount` chroma. (Converts this color to the LCH color space, and then increases the `C` parameter by `amount`.) --- ##### `color.desaturate(amount=10)` The opposite of `color.saturate`; *reduces* color by `amount` chroma. --- ### Creating color scales ##### `spectra.scale(colors)` `colors` should be a list of two or more colors (created by any of the methods above), web-color names, or hexcodes. Returns a `spectra.Scale` object, which translates numbers to their corresponding colors: ```python my_scale = spectra.scale([ "gray", "red" ]) halfway = my_scale(0.5) print(halfway.hexcode) >>> '#c04040' ``` --- ### Modifying color scales ##### `scale.domain(numbers)` By default, a scale's domain is [ 0, 1 ]. But you can change it to be anything else, e.g.: ```python my_scale = spectra.scale([ "gray", "red" ]).domain([ 10, 20 ]) halfway = my_scale(15) print(halfway.hexcode) >>> '#c04040' ``` --- ### Creating color ranges ##### `scale.range(count)` This function returns a list of `spectra.Color` objects evenly spaced between a scale's `colors`. For example: ```python my_scale = spectra.scale([ "gray", "red" ]) my_range = my_scale.range(5) print(my_range) >>> [, , , , ] print([ c.hexcode for c in my_range ]) >>> ['#808080', '#a06060', '#c04040', '#df2020', '#ff0000'] ``` Alternatively, as a shortcut, you can use `spectra.range(colors, count)`. --- ## Feedback/Suggestions Issues and pull requests very much appreciated. spectra-0.0.11/requirements.txt0000644000076500000240000000002113231775400021276 0ustar jeremysinger-vinestaff00000000000000colormath>=3.0.0 spectra-0.0.11/setup.cfg0000644000076500000240000000007313231775613017650 0ustar jeremysinger-vinestaff00000000000000[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 spectra-0.0.11/setup.py0000644000076500000240000000250713225555670017547 0ustar jeremysinger-vinestaff00000000000000import sys, os from setuptools import setup, find_packages import subprocess NAME = "spectra" HERE = os.path.abspath(os.path.dirname(__file__)) version_ns = {} with open(os.path.join(HERE, NAME, "_version.py")) as f: exec(f.read(), {}, version_ns) with open(os.path.join(HERE, "requirements.txt")) as f: reqs = f.read().strip().split("\n") setup( name=NAME, version=version_ns["__version__"], description="Color scales and color conversion made easy for Python.", long_description="", classifiers=[ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3.1", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6" ], keywords="color colors colorspace scale spectrum", author="Jeremy Singer-Vine", author_email="jsvine@gmail.com", url="http://github.com/jsvine/spectra", license="MIT", packages=find_packages(exclude=["test",]), namespace_packages=[], include_package_data=False, zip_safe=False, install_requires=reqs, tests_require=[], test_suite="test" ) spectra-0.0.11/spectra/0000755000076500000240000000000013231775613017470 5ustar jeremysinger-vinestaff00000000000000spectra-0.0.11/spectra/__init__.py0000644000076500000240000000776313111677505021614 0ustar jeremysinger-vinestaff00000000000000from .core import COLOR_SPACES, Color, Scale from ._version import __version__ def lab(L, a, b): """ Create a spectra.Color object in the CIELAB color space. :param float L: L coordinate. :param float a: a coordinate. :param float b: b coordinate. :rtype: Color :returns: A spectra.Color object in the CIELAB color space. """ return Color("lab", L, a, b) def lch(L, c, h): """ Create a spectra.Color object in the CIE LCH color space. Based on `colormath`'s LCHabColor class. :param float L: L coordinate. :param float c: c coordinate. :param float h: h coordinate. :rtype: Color :returns: A spectra.Color object in the CIE LCH color space. """ return Color("lch", L, c, h) def xyz(x, y, z): """ Create a spectra.Color object in the XYZ color space. Based on `colormath`'s LCHabColor class. :param float x: x coordinate. :param float y: y coordinate. :param float z: z coordinate. :rtype: Color :returns: A spectra.Color object in the XYZ color space. """ return Color("xyz", x, y, z) def rgb(r, g, b): """ Create a spectra.Color object in the sRGB color space. Per `colormath`: "If you pass in upscaled values, we automatically scale them down to 0.0-1.0." :param float r: r coordinate. :param float g: g coordinate. :param float b: b coordinate. :rtype: Color :returns: A spectra.Color object in the sRGB color space. """ return Color("rgb", r, g, b) def cmyk(c, m, y, k): """ Create a spectra.Color object in the CMYK color space. :param float c: c coordinate. :param float m: m coordinate. :param float y: y coordinate. :param float k: k coordinate. :rtype: Color :returns: A spectra.Color object in the CMYK color space. """ return Color("cmyk", c, m, y, k) def cmy(c, m, y): """ Create a spectra.Color object in the CMY color space. :param float c: c coordinate. :param float m: m coordinate. :param float y: y coordinate. :rtype: Color :returns: A spectra.Color object in the CMY color space. """ return Color("cmyk", c, m, y) def hsl(h, s, l): """ Create a spectra.Color object in the HSL color space. :param float h: h coordinate. :param float s: s coordinate. :param float l: l coordinate. :rtype: Color :returns: A spectra.Color object in the HSL color space. """ return Color("hsl", h, s, l) def hsv(h, s, v): """ Create a spectra.Color object in the HSV color space. :param float h: h coordinate. :param float s: s coordinate. :param float v: v coordinate. :rtype: Color :returns: A spectra.Color object in the HSV color space. """ return Color("hsv", h, s, v) def html(html_string): """ Create an RGB spectra.Color object from a web-color or hexcode. E.g.: "papayawhip", "#FFF", "#ffffff", "FFEFD5" :param str html_string: Web-color or hexcode. :rtype: Color :returns: A spectra.Color object in the sRGB color space. """ return Color.from_html(html_string) def scale(colors): """ Create a color scale, based on a list of spectra.Color objects. The most common use-case involves passing two colors (a start and an end), but any number of colors is acceptable. Instead of spectra.Color objects, you can also pass a list of web-color or hexcode strings. :param list colors: spectra.Color objects or web-color/hexcode strings. :rtype: Scale :returns: A spectra.Scale object """ return Scale(colors) def range(colors, count): """ Create a range of `count` colors between two or more base colors. Colors should be a list of spectra.Color objects, web-color strings, or hexcode strings. :param list colors: spectra.Color objects or web-color/hexcode strings. :param int count: the number of colors to return. :rtype: list :returns: A list of `count` spectra.Color objects. """ return Scale(colors).range(count) spectra-0.0.11/spectra/_version.py0000644000076500000240000000011113231775426021661 0ustar jeremysinger-vinestaff00000000000000version_info = (0, 0, 11) __version__ = '.'.join(map(str, version_info)) spectra-0.0.11/spectra/core.py0000644000076500000240000001622412551454124020772 0ustar jeremysinger-vinestaff00000000000000from colormath import color_objects, color_conversions from spectra.grapefruit import Color as GC convert_color = color_conversions.convert_color COLOR_SPACES = { "lab": color_objects.LabColor, "rgb": color_objects.sRGBColor, "lch": color_objects.LCHabColor, "xyz": color_objects.XYZColor, "hsl": color_objects.HSLColor, "hsv": color_objects.HSVColor, "cmy": color_objects.CMYColor, "cmyk": color_objects.CMYKColor } class Color(object): """ Represents a color in a given color space. """ def __init__(self, space, *values): """ :param str space: Name of the color space. """ self.values = values self.space = space self.color_object = COLOR_SPACES[space](*values) _rgb = self.color_object if space == "rgb" else self.to("rgb").color_object self.rgb = _rgb.get_value_tuple() self.clamped_rgb = (_rgb.clamped_rgb_r, _rgb.clamped_rgb_g, _rgb.clamped_rgb_b) self.rbg_clamped = self.clamped_rgb @classmethod def from_html(cls, html_string): """ Create sRGB color from a web-color name or hexcode. :param str html_string: Web-color name or hexcode. :rtype: Color :returns: A spectra.Color in the sRGB color space. """ rgb = GC.NewFromHtml(html_string).rgb return cls("rgb", *rgb) def to(self, space): """ Convert color to a different color space. :param str space: Name of the color space. :rtype: Color :returns: A new spectra.Color in the given color space. """ if space == self.space: return self new_color = convert_color(self.color_object, COLOR_SPACES[space]) return self.__class__(space, *new_color.get_value_tuple()) @property def hexcode(self): """ Get this color's corresponding RGB hex. :rtype: str :returns: A six-character string. """ return COLOR_SPACES["rgb"](*self.clamped_rgb).get_rgb_hex() def blend(self, other, ratio=0.5): """ Blend this color with another color in the same color space. By default, blends the colors half-and-half (ratio: 0.5). :param Color other: The color to blend. :param float ratio: How much to blend (0 -> 1). :rtype: Color :returns: A new spectra.Color """ keep = 1.0 - ratio if not self.space == other.space: raise Exception("Colors must belong to the same color space.") values = tuple(((u * keep) + (v * ratio) for u, v in zip(self.values, other.values))) return self.__class__(self.space, *values) def brighten(self, amount=10): """ Brighten this color by `amount` luminance. Converts this color to the LCH color space, and then increases the `L` parameter by `amount`. :param float amount: Amount to increase the luminance. :rtype: Color :returns: A new spectra.Color """ lch = self.to("lch") l, c, h = lch.values new_lch = self.__class__("lch", l + amount, c, h) return new_lch.to(self.space) def darken(self, amount=10): """ Darken this color by `amount` luminance. Converts this color to the LCH color space, and then decreases the `L` parameter by `amount`. :param float amount: Amount to decrease the luminance. :rtype: Color :returns: A new spectra.Color """ return self.brighten(amount=-amount) def saturate(self, amount=10): """ Saturate this color by `amount` chroma. Converts this color to the LCH color space, and then increases the `C` parameter by `amount`. :param float amount: Amount to increase the chroma. :rtype: Color :returns: A new spectra.Color """ lch = self.to("lch") l, c, h = lch.values new_lch = self.__class__("lch", l, c + amount, h) return new_lch.to(self.space) def desaturate(self, amount=10): """ Desaturate this color by `amount` chroma. Converts this color to the LCH color space, and then decreases the `C` parameter by `amount`. :param float amount: Amount to decrease the chroma. :rtype: Color :returns: A new spectra.Color """ return self.saturate(amount=-amount) class Scale(object): """ Represents a color scale. """ def __init__(self, colors, domain=None): """ :param list colors: List of two or more spectra.Colors, or web-color/hexcode strings. :param domain: List of two or more numbers. :type domain: list or None """ _colors = [ c if isinstance(c, Color) else Color.from_html(c) for c in colors ] self.colors = _colors # Set domain n = len(_colors) self._domain = domain or [ float(x) / (n - 1) for x in range(n) ] # Check whether domain is correct length. if len(self._domain) != n: raise ValueError("len(domain) must equal len(colors)") def __call__(self, number): """ Return the color corresponding to the given `number`. :param float number: The number to color-ify. :rtype: Color :returns: A spectra.Color """ if number < self._domain[0] or number > self._domain[-1]: msg = "Number ({0}) not in domain ({1} -> {2})." raise ValueError(msg.format(number, self._domain[0], self._domain[-1])) segments = zip(self._domain[:-1], self._domain[1:]) for i, seg in enumerate(segments): x0, x1 = seg if number >= x0 and number <= x1: num_range = x1 - x0 prop = float(number - x0) / num_range return self.colors[i].blend(self.colors[i+1], prop) def domain(self, domain): """ Create a new scale with the given domain. :param list domain: A list of floats. :rtype: Scale :returns: A new color.Scale object. """ return self.__class__(self.colors, domain) def get_domain(self): """ List this scale's domain. :rtype: list :returns: A list of numbers. """ return self._domain def colorspace(self, space): """ Create a new scale in the given color space. :param str space: The new color space. :rtype: Scale :returns: A new color.Scale object. """ new_colors = [ c.to(space) for c in self.colors ] return self.__class__(new_colors, self._domain) def range(self, count): """ Create a list of colors evenly spaced along this scale's domain. :param int count: The number of colors to return. :rtype: list :returns: A list of spectra.Color objects. """ if count <= 1: raise ValueError("Range size must be greater than 1.") dom = self._domain distance = dom[-1] - dom[0] props = [ self(dom[0] + distance * float(x)/(count-1)) for x in range(count) ] return props spectra-0.0.11/spectra/grapefruit.py0000640000076500000240000015413012503334114022176 0ustar jeremysinger-vinestaff00000000000000#!/usr/bin/python # -*- coding: utf-8 -*-# # Copyright (c) 2008, 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''' from __future__ import division import sys # $Id$ __author__ = 'Xavier Basty ' __version__ = '0.1a3' # The default white reference, use 2° Standard Observer, D65 (daylight) _DEFAULT_WREF = (0.95043, 1.00000, 1.08890) _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) class Color: '''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.NewFromRgb(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.ComplementaryColor(mode='rgb') >>> print(compl.hsl) (210.0, 1.0, 0.5) To directly convert RGB values to their HSL equivalent: >>> h, s, l = Color.RgbToHsl(r, g, b) ''' WHITE_REFERENCE = { 'std_A' : (1.09847, 1.00000, 0.35582), 'std_B' : (0.99093, 1.00000, 0.85313), 'std_C' : (0.98071, 1.00000, 1.18225), 'std_D50' : (0.96421, 1.00000, 0.82519), 'std_D55' : (0.95680, 1.00000, 0.92148), 'std_D65' : (0.95043, 1.00000, 1.08890), 'std_D75' : (0.94972, 1.00000, 1.22639), 'std_E' : (1.00000, 1.00000, 1.00000), 'std_F1' : (0.92834, 1.00000, 1.03665), 'std_F2' : (0.99145, 1.00000, 0.67316), 'std_F3' : (1.03753, 1.00000, 0.49861), 'std_F4' : (1.09147, 1.00000, 0.38813), 'std_F5' : (0.90872, 1.00000, 0.98723), 'std_F6' : (0.97309, 1.00000, 0.60191), 'std_F7' : (0.95017, 1.00000, 1.08630), 'std_F8' : (0.96413, 1.00000, 0.82333), 'std_F9' : (1.00365, 1.00000, 0.67868), 'std_F10' : (0.96174, 1.00000, 0.81712), 'std_F11' : (1.00899, 1.00000, 0.64262), 'std_F12' : (1.08046, 1.00000, 0.39228), 'sup_A' : (1.11142, 1.00000, 0.35200), 'sup_B' : (0.99178, 1.00000, 0.84349), 'sup_C' : (0.97286, 1.00000, 1.16145), 'sup_D50' : (0.96721, 1.00000, 0.81428), 'sup_D55' : (0.95797, 1.00000, 0.90925), 'sup_D65' : (0.94810, 1.00000, 1.07305), 'sup_D75' : (0.94417, 1.00000, 1.20643), 'sup_E' : (1.00000, 1.00000, 1.00000), 'sup_F1' : (0.94791, 1.00000, 1.03191), 'sup_F2' : (1.03245, 1.00000, 0.68990), 'sup_F3' : (1.08968, 1.00000, 0.51965), 'sup_F4' : (1.14961, 1.00000, 0.40963), 'sup_F5' : (0.93369, 1.00000, 0.98636), 'sup_F6' : (1.02148, 1.00000, 0.62074), 'sup_F7' : (0.95780, 1.00000, 1.07618), 'sup_F8' : (0.97115, 1.00000, 0.81135), 'sup_F9' : (1.02116, 1.00000, 0.67826), 'sup_F10' : (0.99001, 1.00000, 0.83134), 'sup_F11' : (1.03820, 1.00000, 0.65555), 'sup_F12' : (1.11428, 1.00000, 0.40353)} 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 __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 = values self.__hsl = Color.RgbToHsl(*values) elif mode=='hsl': self.__hsl = values self.__rgb = Color.HslToRgb(*values) else: raise ValueError('Invalid color mode: ' + mode) self.__a = alpha self.__wref = wref 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) 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 str(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 '(%g, %g, %g, %g)' % (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 unicode('%g, %g, %g, %g)') % (self.__rgb + (self.__a,)) def __iter__(self): return iter(self.__rgb + (self.__a,)) def __len__(self): return 4 def __GetIsLegal(self): return all(0.0 <= v <= 1.0 for v in self) isLegal = property(fget=__GetIsLegal, doc='Boolean indicating whether the color is within the legal gamut.') def __GetNearestLegal(self): def clamp(x, lo, hi): if x < lo: return lo elif x > hi: return hi else: return x return Color.NewFromRgb(*[clamp(v, 0.0, 1.0) for v in self]) nearestLegal = property(fget=__GetNearestLegal, doc='The nearest legal color.') @staticmethod def RgbToHsl(r, g, b): '''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] >>> Color.RgbToHsl(1, 0.5, 0) (30.0, 1.0, 0.5) ''' 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) @staticmethod def _HueToRgb(n1, n2, h): 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 @staticmethod def HslToRgb(h, s, l): '''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] >>> Color.HslToRgb(30.0, 1.0, 0.5) (1.0, 0.5, 0.0) ''' 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 = Color._HueToRgb r = hueToRgb(n1, n2, h + 2) g = hueToRgb(n1, n2, h) b = hueToRgb(n1, n2, h - 2) return (r, g, b) @staticmethod def RgbToHsv(r, g, b): '''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] >>> Color.RgbToHsv(1, 0.5, 0) (30.0, 1.0, 1.0) ''' 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) @staticmethod def HsvToRgb(h, s, v): '''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] >>> Color.HslToRgb(30.0, 1.0, 0.5) (1.0, 0.5, 0.0) ''' 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) @staticmethod def RgbToYiq(r, g, b): '''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)' % Color.RgbToYiq(1, 0.5, 0) '(0.592263, 0.458874, -0.0499818)' ''' 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) @staticmethod def YiqToRgb(y, i, q): '''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] >>> '(%g, %g, %g)' % Color.YiqToRgb(0.592263, 0.458874, -0.0499818) '(1, 0.5, 5.442e-07)' ''' 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) @staticmethod def RgbToYuv(r, g, b): '''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)' % Color.RgbToYuv(1, 0.5, 0) '(0.5925, -0.29156, 0.357505)' ''' 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) @staticmethod def YuvToRgb(y, u, v): '''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)' % Color.YuvToRgb(0.5925, -0.2916, 0.3575) '(0.999989, 0.500015, -6.3276e-05)' ''' r = y + (v * 1.13983) g = y - (u * 0.39465) - (v * 0.58060) b = y + (u * 2.03211) return (r, g, b) @staticmethod def RgbToXyz(r, g, b): '''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)' % Color.RgbToXyz(1, 0.5, 0) '(0.488941, 0.365682, 0.0448137)' ''' 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) @staticmethod def XyzToRgb(x, y, z): '''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)' % Color.XyzToRgb(0.488941, 0.365682, 0.0448137) '(1, 0.5, 6.81883e-08)' ''' 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))) @staticmethod def XyzToLab(x, y, z, 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)' % Color.XyzToLab(0.488941, 0.365682, 0.0448137) '(66.9518, 0.43084, 0.739692)' >>> '(%g, %g, %g)' % Color.XyzToLab(0.488941, 0.365682, 0.0448137, Color.WHITE_REFERENCE['std_D50']) '(66.9518, 0.411663, 0.67282)' ''' # 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) @staticmethod def LabToXyz(l, a, b, 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)' % Color.LabToXyz(66.9518, 0.43084, 0.739692) '(0.488941, 0.365682, 0.0448137)' >>> '(%g, %g, %g)' % Color.LabToXyz(66.9518, 0.411663, 0.67282, Color.WHITE_REFERENCE['std_D50']) '(0.488941, 0.365682, 0.0448138)' ''' 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))) @staticmethod def CmykToCmy(c, m, y, k): '''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)' % Color.CmykToCmy(1, 0.32, 0, 0.5) '(1, 0.66, 0.5)' ''' mk = 1-k return ((c*mk + k), (m*mk + k), (y*mk + k)) @staticmethod def CmyToCmyk(c, m, y): '''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)' % Color.CmyToCmyk(1, 0.66, 0.5) '(1, 0.32, 0, 0.5)' ''' k = min(c, m, y) if k==1.0: return (0.0, 0.0, 0.0, 1.0) mk = 1-k return ((c-k) / mk, (m-k) / mk, (y-k) / mk, k) @staticmethod def RgbToCmy(r, g, b): '''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] >>> Color.RgbToCmy(1, 0.5, 0) (0, 0.5, 1) ''' return (1-r, 1-g, 1-b) @staticmethod def CmyToRgb(c, m, y): '''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] >>> Color.CmyToRgb(0, 0.5, 1) (1, 0.5, 0) ''' return (1-c, 1-m, 1-y) @staticmethod def RgbToIntTuple(r, g, b): '''Convert the color from (r, g, b) to an int tuple. 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] >>> Color.RgbToIntTuple(1, 0.5, 0) (255, 128, 0) ''' return tuple(int(round(v*255)) for v in (r, g, b)) @staticmethod def IntTupleToRgb(intTuple): '''Convert a tuple of ints to (r, g, b). Parameters: The color as an (r, g, b) integer tuple in the range: r[0...255], g[0...255], b[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)' % Color.IntTupleToRgb((255, 128, 0)) '(1, 0.501961, 0)' ''' return tuple(v / 255 for v in intTuple) @staticmethod def RgbToHtml(r, g, b): '''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). >>> Color.RgbToHtml(1, 0.5, 0) '#ff8000' ''' return '#%02x%02x%02x' % tuple((min(round(v*255), 255) for v in (r, g, b))) @staticmethod def HtmlToRgb(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)' % Color.HtmlToRgb('#ff8000') '(1, 0.501961, 0)' >>> '(%g, %g, %g)' % Color.HtmlToRgb('ff8000') '(1, 0.501961, 0)' >>> '(%g, %g, %g)' % Color.HtmlToRgb('#f60') '(1, 0.4, 0)' >>> '(%g, %g, %g)' % Color.HtmlToRgb('f60') '(1, 0.4, 0)' >>> '(%g, %g, %g)' % Color.HtmlToRgb('lemonchiffon') '(1, 0.980392, 0.803922)' ''' html = html.strip().lower() if html[0]=='#': html = html[1:] elif html in Color.NAMED_COLOR: html = Color.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)) @staticmethod def RgbToPil(r, g, b): '''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' % Color.RgbToPil(1, 0.5, 0) '0x0080ff' ''' r, g, b = [min(int(round(v*255)), 255) for v in (r, g, b)] return (b << 16) + (g << 8) + r @staticmethod def PilToRgb(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)' % Color.PilToRgb(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))) @staticmethod def _WebSafeComponent(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 @staticmethod def RgbToWebSafe(r, g, b, 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)' % Color.RgbToWebSafe(1, 0.55, 0.0) '(1, 0.6, 0)' ''' webSafeComponent = Color._WebSafeComponent return tuple((webSafeComponent(v, alt) for v in (r, g, b))) @staticmethod def RgbToGreyscale(r, g, b): '''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)' % Color.RgbToGreyscale(1, 0.8, 0) '(0.6, 0.6, 0.6)' ''' v = (r + g + b) / 3.0 return (v, v, v) @staticmethod def RgbToRyb(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. >>> Color.RgbToRyb(15) 26.0 ''' d = hue % 15 i = int(hue / 15) x0 = _RybWheel[i] x1 = _RybWheel[i+1] return x0 + (x1-x0) * d / 15 @staticmethod def RybToRgb(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. >>> Color.RybToRgb(15) 8.0 ''' d = hue % 15 i = int(hue / 15) x0 = _RgbWheel[i] x1 = _RgbWheel[i+1] return x0 + (x1-x0) * d / 15 @staticmethod def NewFromRgb(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.NewFromRgb(1.0, 0.5, 0.0) (1.0, 0.5, 0.0, 1.0) >>> Color.NewFromRgb(1.0, 0.5, 0.0, 0.5) (1.0, 0.5, 0.0, 0.5) ''' return Color((r, g, b), 'rgb', alpha, wref) @staticmethod def NewFromHsl(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.NewFromHsl(30, 1, 0.5) (1.0, 0.5, 0.0, 1.0) >>> Color.NewFromHsl(30, 1, 0.5, 0.5) (1.0, 0.5, 0.0, 0.5) ''' return Color((h, s, l), 'hsl', alpha, wref) @staticmethod def NewFromHsv(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.NewFromHsv(30, 1, 1) (1.0, 0.5, 0.0, 1.0) >>> Color.NewFromHsv(30, 1, 1, 0.5) (1.0, 0.5, 0.0, 0.5) ''' h2, s, l = Color.RgbToHsl(*Color.HsvToRgb(h, s, v)) return Color((h, s, l), 'hsl', alpha, wref) @staticmethod def NewFromYiq(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. >>> str(Color.NewFromYiq(0.5922, 0.45885,-0.05)) '(0.999902, 0.499955, -6.6905e-05, 1)' >>> str(Color.NewFromYiq(0.5922, 0.45885,-0.05, 0.5)) '(0.999902, 0.499955, -6.6905e-05, 0.5)' ''' return Color(Color.YiqToRgb(y, i, q), 'rgb', alpha, wref) @staticmethod def NewFromYuv(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. >>> str(Color.NewFromYuv(0.5925, -0.2916, 0.3575)) '(0.999989, 0.500015, -6.3276e-05, 1)' >>> str(Color.NewFromYuv(0.5925, -0.2916, 0.3575, 0.5)) '(0.999989, 0.500015, -6.3276e-05, 0.5)' ''' return Color(Color.YuvToRgb(y, u, v), 'rgb', alpha, wref) @staticmethod def NewFromXyz(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. >>> str(Color.NewFromXyz(0.488941, 0.365682, 0.0448137)) '(1, 0.5, 6.81883e-08, 1)' >>> str(Color.NewFromXyz(0.488941, 0.365682, 0.0448137, 0.5)) '(1, 0.5, 6.81883e-08, 0.5)' ''' return Color(Color.XyzToRgb(x, y, z), 'rgb', alpha, wref) @staticmethod def NewFromLab(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. >>> str(Color.NewFromLab(66.9518, 0.43084, 0.739692)) '(1, 0.5, 1.09491e-08, 1)' >>> str(Color.NewFromLab(66.9518, 0.43084, 0.739692, wref=Color.WHITE_REFERENCE['std_D50'])) '(1.01238, 0.492011, -0.14311, 1)' >>> str(Color.NewFromLab(66.9518, 0.43084, 0.739692, 0.5)) '(1, 0.5, 1.09491e-08, 0.5)' >>> str(Color.NewFromLab(66.9518, 0.43084, 0.739692, 0.5, Color.WHITE_REFERENCE['std_D50'])) '(1.01238, 0.492011, -0.14311, 0.5)' ''' return Color(Color.XyzToRgb(*Color.LabToXyz(l, a, b, wref)), 'rgb', alpha, wref) @staticmethod def NewFromCmy(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.NewFromCmy(0, 0.5, 1) (1, 0.5, 0, 1.0) >>> Color.NewFromCmy(0, 0.5, 1, 0.5) (1, 0.5, 0, 0.5) ''' return Color(Color.CmyToRgb(c, m, y), 'rgb', alpha, wref) @staticmethod def NewFromCmyk(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. >>> str(Color.NewFromCmyk(1, 0.32, 0, 0.5)) '(0, 0.34, 0.5, 1)' >>> str(Color.NewFromCmyk(1, 0.32, 0, 0.5, 0.5)) '(0, 0.34, 0.5, 0.5)' ''' return Color(Color.CmyToRgb(*Color.CmykToCmy(c, m, y, k)), 'rgb', alpha, wref) @staticmethod def NewFromHtml(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. >>> str(Color.NewFromHtml('#ff8000')) '(1, 0.501961, 0, 1)' >>> str(Color.NewFromHtml('ff8000')) '(1, 0.501961, 0, 1)' >>> str(Color.NewFromHtml('#f60')) '(1, 0.4, 0, 1)' >>> str(Color.NewFromHtml('f60')) '(1, 0.4, 0, 1)' >>> str(Color.NewFromHtml('lemonchiffon')) '(1, 0.980392, 0.803922, 1)' >>> str(Color.NewFromHtml('#ff8000', 0.5)) '(1, 0.501961, 0, 0.5)' ''' return Color(Color.HtmlToRgb(html), 'rgb', alpha, wref) @staticmethod def NewFromPil(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. >>> str(Color.NewFromPil(0x0080ff)) '(1, 0.501961, 0, 1)' >>> str(Color.NewFromPil(0x0080ff, 0.5)) '(1, 0.501961, 0, 0.5)' ''' return Color(Color.PilToRgb(pil), 'rgb', alpha, wref) def __GetAlpha(self): return self.__a alpha = property(fget=__GetAlpha, doc='The transparency of this color. 0.0 is transparent and 1.0 is fully opaque.') def __GetWRef(self): return self.__wref whiteRef = property(fget=__GetWRef, doc='the white reference point of this color.') def __GetRGB(self): return self.__rgb rgb = property(fget=__GetRGB, doc='The RGB values of this Color.') def __GetHue(self): return self.__hsl[0] hue = property(fget=__GetHue, doc='The hue of this color.') def __GetHSL(self): return self.__hsl hsl = property(fget=__GetHSL, doc='The HSL values of this Color.') def __GetHSV(self): h, s, v = Color.RgbToHsv(*self.__rgb) return (self.__hsl[0], s, v) hsv = property(fget=__GetHSV, doc='The HSV values of this Color.') def __GetYIQ(self): return Color.RgbToYiq(*self.__rgb) yiq = property(fget=__GetYIQ, doc='The YIQ values of this Color.') def __GetYUV(self): return Color.RgbToYuv(*self.__rgb) yuv = property(fget=__GetYUV, doc='The YUV values of this Color.') def __GetXYZ(self): return Color.RgbToXyz(*self.__rgb) xyz = property(fget=__GetXYZ, doc='The CIE-XYZ values of this Color.') def __GetLAB(self): return Color.XyzToLab(wref=self.__wref, *Color.RgbToXyz(*self.__rgb)) lab = property(fget=__GetLAB, doc='The CIE-LAB values of this Color.') def __GetCMY(self): return Color.RgbToCmy(*self.__rgb) cmy = property(fget=__GetCMY, doc='The CMY values of this Color.') def __GetCMYK(self): return Color.CmyToCmyk(*Color.RgbToCmy(*self.__rgb)) cmyk = property(fget=__GetCMYK, doc='The CMYK values of this Color.') def __GetIntTuple(self): return Color.RgbToIntTuple(*self.__rgb) intTuple = property(fget=__GetIntTuple, doc='This Color as a tuple of integers in the range [0...255]') def __GetHTML(self): return Color.RgbToHtml(*self.__rgb) html = property(fget=__GetHTML, doc='This Color as an HTML color definition.') def __GetPIL(self): return Color.RgbToPil(*self.__rgb) pil = property(fget=__GetPIL, doc='This Color as a PIL compatible value.') def __GetwebSafe(self): return Color.RgbToWebSafe(*self.__rgb) webSafe = property(fget=__GetwebSafe, doc='The web safe color nearest to this one (RGB).') def __GetGreyscale(self): return Color.RgbToGreyscale(*self.rgb) greyscale = property(fget=__GetGreyscale, doc='The greyscale equivalent to this color (RGB).') def ColorWithAlpha(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.NewFromRgb(1.0, 0.5, 0.0, 1.0).ColorWithAlpha(0.5) (1.0, 0.5, 0.0, 0.5) ''' return Color(self.__rgb, 'rgb', alpha, self.__wref) def ColorWithWhiteRef(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.NewFromRgb(1.0, 0.5, 0.0, 1.0, Color.WHITE_REFERENCE['std_D65']) >>> c2 = c.ColorWithWhiteRef(Color.WHITE_REFERENCE['sup_D50']) >>> c2.rgb (1.0, 0.5, 0.0) >>> '(%g, %g, %g)' % c2.whiteRef '(0.96721, 1, 0.81428)' >>> c2 = c.ColorWithWhiteRef(Color.WHITE_REFERENCE['sup_D50'], labAsRef=True) >>> '(%g, %g, %g)' % c2.rgb '(1.01463, 0.490339, -0.148131)' >>> '(%g, %g, %g)' % c2.whiteRef '(0.96721, 1, 0.81428)' >>> '(%g, %g, %g)' % c.lab '(66.9518, 0.43084, 0.739692)' >>> '(%g, %g, %g)' % c2.lab '(66.9518, 0.43084, 0.739693)' ''' if labAsRef: l, a, b = self.__GetLAB() return Color.NewFromLab(l, a, b, self.__a, wref) else: return Color(self.__rgb, 'rgb', self.__a, wref) def ColorWithHue(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.NewFromHsl(30, 1, 0.5).ColorWithHue(60) (1.0, 1.0, 0.0, 1.0) >>> Color.NewFromHsl(30, 1, 0.5).ColorWithHue(60).hsl (60, 1, 0.5) ''' h, s, l = self.__hsl return Color((hue, s, l), 'hsl', self.__a, self.__wref) def ColorWithSaturation(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.NewFromHsl(30, 1, 0.5).ColorWithSaturation(0.5) (0.75, 0.5, 0.25, 1.0) >>> Color.NewFromHsl(30, 1, 0.5).ColorWithSaturation(0.5).hsl (30, 0.5, 0.5) ''' h, s, l = self.__hsl return Color((h, saturation, l), 'hsl', self.__a, self.__wref) def ColorWithLightness(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.NewFromHsl(30, 1, 0.5).ColorWithLightness(0.25) (0.5, 0.25, 0.0, 1.0) >>> Color.NewFromHsl(30, 1, 0.5).ColorWithLightness(0.25).hsl (30, 1, 0.25) ''' h, s, l = self.__hsl return Color((h, s, lightness), 'hsl', self.__a, self.__wref) def DarkerColor(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.NewFromHsl(30, 1, 0.5).DarkerColor(0.25) (0.5, 0.25, 0.0, 1.0) >>> Color.NewFromHsl(30, 1, 0.5).DarkerColor(0.25).hsl (30, 1, 0.25) ''' h, s, l = self.__hsl return Color((h, s, max(l - level, 0)), 'hsl', self.__a, self.__wref) def LighterColor(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.NewFromHsl(30, 1, 0.5).LighterColor(0.25) (1.0, 0.75, 0.5, 1.0) >>> Color.NewFromHsl(30, 1, 0.5).LighterColor(0.25).hsl (30, 1, 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.NewFromHsl(30, 0.5, 0.5).Saturate(0.25) (0.875, 0.5, 0.125, 1.0) >>> Color.NewFromHsl(30, 0.5, 0.5).Saturate(0.25).hsl (30, 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.NewFromHsl(30, 0.5, 0.5).Desaturate(0.25) (0.625, 0.5, 0.375, 1.0) >>> Color.NewFromHsl(30, 0.5, 0.5).Desaturate(0.25).hsl (30, 0.25, 0.5) ''' h, s, l = self.__hsl return Color((h, max(s - level, 0), l), 'hsl', self.__a, self.__wref) def WebSafeDither(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.NewFromRgb(1.0, 0.45, 0.0) >>> c1, c2 = c.WebSafeDither() >>> str(c1) '(1, 0.4, 0, 1)' >>> str(c2) '(1, 0.6, 0, 1)' ''' return ( Color(Color.RgbToWebSafe(*self.__rgb), 'rgb', self.__a, self.__wref), Color(Color.RgbToWebSafe(alt=True, *self.__rgb), 'rgb', self.__a, self.__wref)) def 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.NewFromRgb(1.0, 0.0, 0.0, alpha=1) >>> c2 = Color.NewFromRgb(0.0, 1.0, 0.0, alpha=0) >>> c1.Gradient(c2, 3) [(0.75, 0.25, 0.0, 0.75), (0.5, 0.5, 0.0, 0.5), (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 ComplementaryColor(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.NewFromHsl(30, 1, 0.5).ComplementaryColor(mode='rgb') (0.0, 0.5, 1.0, 1.0) >>> Color.NewFromHsl(30, 1, 0.5).ComplementaryColor(mode='rgb').hsl (210, 1, 0.5) ''' h, s, l = self.__hsl if mode == 'ryb': h = Color.RgbToRyb(h) h = (h+180)%360 if mode == 'ryb': h = Color.RybToRgb(h) return Color((h, s, l), 'hsl', self.__a, self.__wref) def MonochromeScheme(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.NewFromHsl(30, 0.5, 0.5) >>> ['(%g, %g, %g)' % clr.hsl for clr in c.MonochromeScheme()] ['(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 TriadicScheme(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.NewFromHsl(30, 1, 0.5) >>> c2, c3 = c1.TriadicScheme(mode='rgb') >>> c2.hsl (150.0, 1, 0.5) >>> c3.hsl (270.0, 1, 0.5) >>> c2, c3 = c1.TriadicScheme(angle=40, mode='rgb') >>> c2.hsl (190.0, 1, 0.5) >>> c3.hsl (230.0, 1, 0.5) ''' h, s, l = self.__hsl angle = min(angle, 120) / 2.0 if mode == 'ryb': h = Color.RgbToRyb(h) h += 180 h1 = (h - angle) % 360 h2 = (h + angle) % 360 if mode == 'ryb': h1 = Color.RybToRgb(h1) h2 = Color.RybToRgb(h2) return ( Color((h1, s, l), 'hsl', self.__a, self.__wref), Color((h2, s, l), 'hsl', self.__a, self.__wref)) def TetradicScheme(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.NewFromHsl(30, 1, 0.5) >>> [c.hsl for c in col.TetradicScheme(mode='rgb', angle=30)] [(90, 1, 0.5), (210, 1, 0.5), (270, 1, 0.5)] ''' h, s, l = self.__hsl if mode == 'ryb': h = Color.RgbToRyb(h) h1 = (h + 90 - angle) % 360 h2 = (h + 180) % 360 h3 = (h + 270 - angle) % 360 if mode == 'ryb': h1 = Color.RybToRgb(h1) h2 = Color.RybToRgb(h2) h3 = Color.RybToRgb(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 AnalogousScheme(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.NewFromHsl(30, 1, 0.5) >>> c2, c3 = c1.AnalogousScheme(angle=60, mode='rgb') >>> c2.hsl (330, 1, 0.5) >>> c3.hsl (90, 1, 0.5) >>> c2, c3 = c1.AnalogousScheme(angle=10, mode='rgb') >>> c2.hsl (20, 1, 0.5) >>> c3.hsl (40, 1, 0.5) ''' h, s, l = self.__hsl if mode == 'ryb': h = Color.RgbToRyb(h) h += 360 h1 = (h - angle) % 360 h2 = (h + angle) % 360 if mode == 'ryb': h1 = Color.RybToRgb(h1) h2 = Color.RybToRgb(h2) return (Color((h1, s, l), 'hsl', self.__a, self.__wref), Color((h2, s, l), 'hsl', self.__a, self.__wref)) def AlphaBlend(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.NewFromRgb(1, 0.5, 0, 0.2) >>> c2 = Color.NewFromRgb(1, 1, 1, 0.8) >>> c3 = c1.AlphaBlend(c2) >>> str(c3) '(1, 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.NewFromRgb(1, 0.5, 0, 0.2) >>> c2 = Color.NewFromRgb(1, 1, 1, 0.6) >>> c3 = c1.Blend(c2) >>> str(c3) '(1, 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 spectra-0.0.11/spectra.egg-info/0000755000076500000240000000000013231775613021162 5ustar jeremysinger-vinestaff00000000000000spectra-0.0.11/spectra.egg-info/dependency_links.txt0000644000076500000240000000000113231775612025227 0ustar jeremysinger-vinestaff00000000000000 spectra-0.0.11/spectra.egg-info/namespace_packages.txt0000644000076500000240000000000113231775612025503 0ustar jeremysinger-vinestaff00000000000000 spectra-0.0.11/spectra.egg-info/not-zip-safe0000644000076500000240000000000112503555367023413 0ustar jeremysinger-vinestaff00000000000000 spectra-0.0.11/spectra.egg-info/PKG-INFO0000644000076500000240000000136413231775612022262 0ustar jeremysinger-vinestaff00000000000000Metadata-Version: 1.1 Name: spectra Version: 0.0.11 Summary: Color scales and color conversion made easy for Python. Home-page: http://github.com/jsvine/spectra Author: Jeremy Singer-Vine Author-email: jsvine@gmail.com License: MIT Description: UNKNOWN Keywords: color colors colorspace scale spectrum Platform: UNKNOWN Classifier: Development Status :: 3 - Alpha Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3.1 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 spectra-0.0.11/spectra.egg-info/requires.txt0000644000076500000240000000002013231775612023551 0ustar jeremysinger-vinestaff00000000000000colormath>=3.0.0spectra-0.0.11/spectra.egg-info/SOURCES.txt0000644000076500000240000000057413231775613023054 0ustar jeremysinger-vinestaff00000000000000LICENSE.txt MANIFEST.in README.md requirements.txt setup.py spectra/__init__.py spectra/_version.py spectra/core.py spectra/grapefruit.py spectra.egg-info/PKG-INFO spectra.egg-info/SOURCES.txt spectra.egg-info/dependency_links.txt spectra.egg-info/namespace_packages.txt spectra.egg-info/not-zip-safe spectra.egg-info/requires.txt spectra.egg-info/top_level.txt test/test_basic.pyspectra-0.0.11/spectra.egg-info/top_level.txt0000644000076500000240000000001013231775612023702 0ustar jeremysinger-vinestaff00000000000000spectra spectra-0.0.11/test/0000755000076500000240000000000013231775613017006 5ustar jeremysinger-vinestaff00000000000000spectra-0.0.11/test/test_basic.py0000644000076500000240000000112612551724312021472 0ustar jeremysinger-vinestaff00000000000000import spectra import nose def test_polylinear(): """ via: https://github.com/jsvine/spectra/issues/4 """ colors = ['yellow', 'red', 'black'] domain = [0, 50, 100] color_scale = spectra.scale(colors).domain(domain) r = color_scale.range(5) results = [ c.hexcode for c in r ] goal = ['#ffff00', '#ff8000', '#ff0000', '#800000', '#000000'] assert(results == goal) @nose.tools.raises(ValueError) def test_polylinear_fail(): colors = ['yellow', 'red', 'black'] domain = [ 0, 50 ] # Domain has one too few items spectra.scale(colors).domain(domain)