9. color
— Playing with colors.¶
This module holds the basic functionality for working with colors in pyFormex.
The Color
class represents a single color. It allows creation of
a color from different inputs and conversion to many outputs.
For working with large sets of colors, there is the colorArray
,
which collects many colors in a numpy.ndarray
, allowing highly
performant computation.
There are functions to handle named colors, convert between color systems,
compute lightness.
A number of legacy functions ar deprecated, as their functionlity can be achieved by using the Color class.
The following table shows the built-in colors, with their name, RGB values in 0..1 range and luminance.
>>> with floatformat('.1f'):
... for k,v in PF_COLORS.items():
... print(f"{k:>15s} = {v} -> {v.luminance():.3f} ({v.lightness():.3f})")
darkgrey = (0.4, 0.4, 0.4) -> 0.133 (0.432)
red = (1.0, 0.0, 0.0) -> 0.213 (0.532)
green = (0.0, 1.0, 0.0) -> 0.715 (0.877)
blue = (0.1, 0.1, 0.8) -> 0.053 (0.275)
cyan = (0.0, 1.0, 1.0) -> 0.787 (0.911)
magenta = (1.0, 0.0, 1.0) -> 0.285 (0.603)
yellow = (1.0, 1.0, 0.0) -> 0.928 (0.971)
white = (1.0, 1.0, 1.0) -> 1.000 (1.000)
black = (0.0, 0.0, 0.0) -> 0.000 (0.000)
darkred = (0.6, 0.0, 0.0) -> 0.068 (0.313)
darkgreen = (0.0, 0.5, 0.0) -> 0.153 (0.461)
darkblue = (0.1, 0.2, 0.6) -> 0.049 (0.264)
darkcyan = (0.0, 0.6, 0.6) -> 0.251 (0.572)
darkmagenta = (0.6, 0.0, 0.6) -> 0.091 (0.361)
darkyellow = (0.6, 0.6, 0.0) -> 0.296 (0.613)
lightgrey = (0.8, 0.8, 0.8) -> 0.604 (0.820)
mediumgrey = (0.6, 0.6, 0.6) -> 0.319 (0.632)
pyformex_pink = (1.0, 0.2, 0.4) -> 0.246 (0.567)
orange = (1.0, 0.5, 0.0) -> 0.366 (0.670)
purple = (0.6, 0.2, 1.0) -> 0.164 (0.474)
brown = (0.6, 0.4, 0.2) -> 0.165 (0.476)
gold = (1.0, 0.8, 0.0) -> 0.644 (0.842)
orchid = (0.8, 0.4, 0.9) -> 0.280 (0.599)
lightlightgrey = (0.9, 0.9, 0.9) -> 0.787 (0.911)
>>> Palette['default']
('darkgrey', 'red', 'green', 'blue', 'cyan', 'magenta', 'yellow', 'white',
'black', 'darkred', 'darkgreen', 'darkblue', 'darkcyan', 'darkmagenta',
'darkyellow', 'lightgrey', 'mediumgrey', 'pyformex_pink', 'orange', 'purple',
'brown', 'gold', 'orchid', 'lightlightgrey')
>>> Palette['dark']
('darkgrey', 'blue', 'black', 'darkred', 'darkgreen', 'darkblue',
'darkmagenta', 'purple', 'brown')
>>> Palette['light']
('red', 'green', 'cyan', 'magenta', 'yellow', 'white', 'darkcyan',
'darkyellow', 'lightgrey', 'mediumgrey', 'pyformex_pink', 'orange', 'gold',
'orchid', 'lightlightgrey')
9.1. Classes defined in module color¶
- class color.Color(*color, alpha=None, clip=False)[source]¶
A class representing a color.
The Color class represents a single color, with or without transparency. The color is internally stored as a tuple of 3 (RGB) or 4 (RGBA) components. Each component is a float value in the range 0.0 to 1.0. Color instances can be initialized from a variety of inputs.
- Parameters:
color –
One, three or four arguments specifying the color. If three or four arguments are given, they are handled as if a single argument
tuple(*colors)
was provided. If only one argument, it must be one of the following:an int: an index into the Color.palette (modulo the palette length)
a float: a grey color with the given intensity
a string with the name of one of the built-in colorset(‘pf’)
a string specifying the X11 name of a color
a hex string ‘#rgb’ or ‘#rrggbb’ with 1 or 2 hexadecimal digits per color
a tuple or list of 3 or 4 integer values in the range 0..255
a tuple or list of 3 or 4 float values in the range 0.0..1.0
alpha (float, optional) – A float in the range 0.0 to 1.0. If provided, forces the creation of a 4-channel color, adding the provided value as the alpha channel if
*color
only contained 3 components. This will not overwrite the alpha channel if*color
already contains 4 components. Overwriting alpha can be achieved withColor(*color[:3], alpha=value)
.clip (bool) – If True, the final values will be clipped to the range 0.0..1.0. If False (default), values outside that range are allowed: OpenGL can make clever use of such values and clip them at render time.
- Raises:
ValueError – If the input is not one of the accepted data.:
Examples
>>> Color(1.0, 1.0, 0.0) == Color([1.0, 1.0, 0.0]) True >>> c = Color(1.0, 0.5, 0.2) >>> c Color(1.0, 0.5, 0.2) >>> c.alpha >>> c.rgb (1.0, 0.5, 0.2) >>> c.rgba (1.0, 0.5, 0.2, 0.5) >>> c.RGB (255, 128, 51) >>> c.RGBA (255, 128, 51, 128) >>> c.web '#ff8033' >>> c.gl array([1. , 0.5, 0.2]) >>> c.gl4 array([1. , 0.5, 0.2, 0.5]) >>> c.name 'chocolate1' >>> c.namediff() ('chocolate1', 0.03398...) >>> c.luminance() 0.3681... >>> Color(2) Color(0.0, 1.0, 0.0) >>> Color('red') Color(1.0, 0.0, 0.0) >>> Color('indianred') Color(0.8039..., 0.3607..., 0.3607...) >>> Color('grey90') Color(0.8980..., 0.8980..., 0.8980...) >>> Color('#ff0000') Color(1.0, 0.0, 0.0) >>> Color("zorro") Traceback (most recent call last): ... pyformex.color.InvalidColor: No color named 'zorro' >>> red Color(1.0, 0.0, 0.0) >>> Color([200,200,255]) Color(0.7843..., 0.7843..., 1.0) >>> Color(np.array([200,200,255], dtype=np.uint8)) Color(0.7843..., 0.7843..., 1.0) >>> Color([1.,1.,1.]) Color(1.0, 1.0, 1.0) >>> Color(0.6) Color(0.6, 0.6, 0.6) >>> Color(0.6, alpha=0.8) Color(0.6, 0.6, 0.6, 0.8) >>> Color(200, 200, 255, 128) Color(0.7843..., 0.7843..., 1.0, 0.5...) >>> Color(200, 200, 255, alpha=0.5) Color(0.7843..., 0.7843..., 1.0, 0.5) >>> Color(200, 200, 255, 255, alpha=0.5) Color(0.7843..., 0.7843..., 1.0, 1.0)
- property rgb¶
Return the red, green and blue component as a tuple
- property rgba¶
Return a tuple (r,g,b,a)
- property RGB¶
Return red, green and blue values as int values
- property RGBA¶
Return RGBA int values
- property gl¶
Return the red, green and blue components as a float array
- property web¶
Convert a Color to hex RGB string (webcolor)
- property name¶
Return a human name for the color
- property fg¶
Return the ANSI escape sequence to set this color as foreground
- property bg¶
Return the ANSI escape sequence to set this color as background
- luminance(gamma=True)[source]¶
Compute the luminance of a color.
- Returns:
float – A value in the range 0.0 (black) to 1.0 (white). The higher the luminance, the brighter the color appears to the human eye. The result is not linear for the human perception though: a luminance twice as high does not mean twice as light. Use
lightness()
if you need a linear response.
Examples
>>> print([f"{Color(c).luminance():0.2f}" ... for c in ['black','red','green','blue']]) ['0.00', '0.21', '0.72', '0.05']
- lightness()[source]¶
Compute the perceived lightness of a color.
- Returns:
float – A float in the range 0.0 (black) to 1.0 (white). A higher lightness means that the color appears brighter to the human eye. This can for example be used to derive a good contrasting foreground color to display text on a colored background. Values lower than 0.5 contrast well with white, larger value contrast better with black. The response is linear: a color with lightness twice as high, will appear twice as bright.
Notes
Traditionally, lightness is often returned on a scale of 0 to 100. We use 0 to 1, as it is more in line with the other color values.
- class color.ansi[source]¶
Ansi escape sequences for colors
Contains attributes: reset, bold, dim, underline, reverse, invisible, crossout and erase_eol. Furthermore, there are two static functions to set foreground and background color: fg and bg, both taking a triple R,G,B values (0..255) as parameters.
9.2. Functions defined in module color¶
- color.colorArray(colors)[source]¶
Create an array of colors.
When working with large collections of colors, colorArray provides important performance benefits over the use of individual Color instances.
- Parameters:
colors (float or str array_like) – One or more colors defined by float tuples rgb or rgba (this includes Color instances) or by common color names.
- Returns:
float ndarray – A float ndarray of shape (…, 3) or (…, 4), depending on the input.
Examples
>>> colorArray('red') array([1., 0., 0.]) >>> colorArray('grey90') array([0.898, 0.898, 0.898]) >>> colorArray(['red', 'green', 'blue']) array([[1. , 0. , 0. ], [0. , 1. , 0. ], [0.1, 0.1, 0.8]]) >>> colorArray([['red', 'green', 'blue'], ['cyan', 'magenta', 'yellow']]) array([[[1. , 0. , 0. ], [0. , 1. , 0. ], [0.1, 0.1, 0.8]], [[0. , 1. , 1. ], [1. , 0. , 1. ], [1. , 1. , 0. ]]]) >>> colorArray((255, 128, 64)) Traceback (most recent call last): ... pyformex.color.InvalidColor: colorArray does not accept integer input
- color.GLcolor(color)[source]¶
Convert color input to a tuple (r,g,b)
This is like Color(color), but invalid colors return (0., 0., 0.)
- color.RGB_web(R, G, B)[source]¶
Convert a tuple(R,G,B) to hex RGB string (webcolor)
Examples
>>> RGB_web(255, 128, 64) '#ff8040'
- color.clipit(value, minvalue=None, maxvalue=None)[source]¶
Clip a float value
>>> clipit(1.7) 1.7 >>> clipit(1.7, minvalue=2.0) 2.0
- color.colorName(color)¶
Return the web color name for a color
- color.colornames(cset=None)[source]¶
Return a list of color names in specified colorset(s).
If cset is str: return pure names If cset is list of str: return prefixed names If cset is None: cset = [all colorsets]
>>> colornames('x11')[0] 'aliceblue' >>> colornames('pf')[0] 'darkgrey' >>> colornames(['pf', 'x11'])[0] 'pf:darkgrey'
- color.colorset(cset)[source]¶
Return a colorset
This provides lazy loading of the colorsets. They are only loaded when used.
>>> d = colorset('x11') >>> type(_COLORSETS_['x11']) <class 'dict'>
- color.colorsets()[source]¶
Return names of available colorsets
>>> colorsets() ('pf', 'x11', 'xkcd')
- color.gamma(c)[source]¶
Apply gamma correction to an rgb channel
Computes the sRGB from a linearized RGB value
See also
https
//en.wikipedia.org/wiki/SRGB
- color.inv_gamma(c)[source]¶
Apply inverse gamma correction to an rgb channel
This is also known as linearizing (or gamma-expanding) the channel
See also
https
//en.wikipedia.org/wiki/SRGB
- color.lightness(Y)[source]¶
Compute perceived lightness L* from linear luminance between 0.0 and 1.0
- Returns:
float – A float in the range 0.0 (black) to 1.0 (white).
Examples
>>> lightness(luminance(red)) array(0.5323) >>> lightness(luminance(['black','red','green','blue'])) array([0. , 0.5323, 0.8774, 0.2754]) >>> lightness(luminance([black,red,green,blue])) array([0. , 0.5323, 0.8774, 0.2754])
- color.luminance(color, gamma=True)[source]¶
Compute the luminance of a color.
- Returns:
float | float array – A value in the range 0.0 (black) to 1.0 (white). The higher the luminance, the brighter the color appears to the human eye. The result is not linear for the human perception though: a luminance twice as high does not mean twice as light. Use
lightness()
to map luminance to perceived lightness.
Examples
>>> luminance(red) 0.2126 >>> luminance(['black','red','green','blue']) array([0. , 0.2126, 0.7152, 0.0529]) >>> luminance([black, mediumgrey, lightgrey, white]) array([0. , 0.3185, 0.6038, 1. ])
- color.named_color(name)[source]¶
Find the webcolor value for the named color
- Parameters:
name (str) – The common name for the color, e.g. ‘darkgreen’. The name may be prefixed by the colorset where the name is to be found. Known colorsets are ‘pf’, ‘x11’, ‘xkcd’. A colorset string part must be followed by a ‘:’. Thus, ‘x11:darkgreen’ is the dark green from the x11 colorset. Multiple colorsets can be specified, like ‘xkcd:x11:darkgreen’, and will be looked up in that order until the first match. No prefix is equivalent to ‘pf:x11:xkcd:’.
- Returns:
str | None – A string with the webcolor designation of the named color or None if no color with that name is found.
Examples
>>> named_color('darkgreen') Color(0.0, 0.5, 0.0) >>> named_color('pf:darkgreen') Color(0.0, 0.5, 0.0) >>> named_color('x11:darkgreen') Color(0.0, 0.3921..., 0.0) >>> named_color('xkcd:darkgreen') Color(0.01960..., 0.2862..., 0.02745...) >>> named_color('banana') Color(1.0, 1.0, 0.4941...) >>> named_color('x11:banana') # no such color
- color.normalize_color_name(col)[source]¶
Normalize a color name.
Different sources use different styles to name colors. This functions normalizes color names by removing spaces and special characters, converting everything to lower case, and converting ‘gray’ to ‘grey’.
Examples
>>> normalize_color_name('DarkGray') 'darkgrey' >>> normalize_color_name(' dark gray') 'darkgrey' >>> normalize_color_name('dark_gray-blue') 'dark_greyblue'
- color.printc(*args, sep=' ', end='\n', file=None, flush=False, color=None, bgcolor=None)[source]¶
Print in color on an ANSI terminal
This is functionally equivalent with Python’s builtin print function, but has two extra parameters that allow printing in color on an ANSI compatible terminal. When the file parameter is used, color is disabled. When color is used, flush is forced to True.
- Parameters:
color (color_like) – The color to be used for the displayed text
bgcolor (color_like) – The backgropund color to be used for the displayed text.
- color.read_webcolors(filename)[source]¶
Read a webcolors file
A webcolors file is a text file where each line contains two strings: a webcolor string in the format #RRGGBB, and a common name for the color. Lines not starting with a ‘#’ are ignored.
- Returns:
dict – A dict with the color names as keys and the webcolors as values.
- color.rgb_xyz(rgb)[source]¶
Convert RGB to CIEXYZ
The rgb values are the linearized rgb values
See also
https
//en.wikipedia.org/wiki/SRGB
- color.web_RGB(color)[source]¶
Convert a webcolor ‘#RRGGBB’ or ‘#RGB’ to (R,G,B) tuple
Examples
>>> web_RGB('#ff6633') (255, 102, 51) >>> web_RGB('#af3') (170, 255, 51)