# vim: set et sw=4 sts=4 fileencoding=utf-8:
#
# The colorzero color library
#
# Copyright (c) 2016-2021 Dave Jones <dave@waveform.org.uk>
#
# SPDX-License-Identifier: BSD-3-Clause
"""
Defines the classes for manipulating the attributes of the :class:`Color`
class through the standard binary operators.
"""
from math import pi
[docs]class Red(float):
"""
Represents the red component of a :class:`Color` for use in
transformations. Instances of this class can be constructed directly with a
float value, or by querying the :attr:`Color.red` attribute. Addition,
subtraction, and multiplication are supported with :class:`Color`
instances. For example::
>>> Color.from_rgb(0, 0, 0) + Red(0.5)
<Color html='#800000' rgb=(0.5, 0, 0)>
>>> Color('#f00') - Color('#900').red
<Color html='#660000' rgb=(0.4, 0, 0)>
>>> (Red(0.1) * Color('red')).red
Red(0.1)
"""
def __repr__(self):
return "Red({:g})".format(self)
[docs]class Green(float):
"""
Represents the green component of a :class:`Color` for use in
transformations. Instances of this class can be constructed directly with
a float value, or by querying the :attr:`Color.green` attribute. Addition,
subtraction, and multiplication are supported with :class:`Color`
instances. For example::
>>> Color(0, 0, 0) + Green(0.1)
<Color html='#001a00' rgb=(0, 0.1, 0)>
>>> Color.from_yuv(1, -0.4, -0.6) - Green(1)
<Color html='#510030' rgb=(0.316098, 0, 0.187156)>
>>> (Green(0.5) * Color('white')).rgb
RGB(r=1.0, g=0.5, b=1.0)
"""
def __repr__(self):
return "Green({:g})".format(self)
[docs]class Blue(float):
"""
Represents the blue component of a :class:`Color` for use in
transformations. Instances of this class can be constructed directly with
a float value, or by querying the :attr:`Color.blue` attribute. Addition,
subtraction, and multiplication are supported with :class:`Color`
instances. For example::
>>> Color(0, 0, 0) + Blue(0.2)
<Color html='#000033' rgb=(0, 0, 0.2)>
>>> Color.from_hls(0.5, 0.5, 1.0) - Blue(1)
<Color html='#00ff00' rgb=(0, 1, 0)>
>>> Blue(0.9) * Color('white')
<Color html='#ffffe6' rgb=(1, 1, 0.9)>
"""
def __repr__(self):
return "Blue({:g})".format(self)
[docs]class Hue(float):
"""
Represents the hue of a :class:`Color` for use in transformations.
Instances of this class can be constructed directly with a float value in
the range [0.0, 1.0) representing an angle around the `HSL hue wheel`_. As
this is a circular mapping, 0.0 and 1.0 effectively mean the same thing,
i.e. out of range values will be normalized into the range [0.0, 1.0).
The class can also be constructed with the keyword arguments ``deg`` or
``rad`` if you wish to specify the hue value in degrees or radians instead,
respectively. Instances can also be constructed by querying the
:attr:`Color.hue` attribute.
Addition, subtraction, and multiplication are supported with :class:`Color`
instances. For example::
>>> Color(1, 0, 0).hls
HLS(h=0.0, l=0.5, s=1.0)
>>> (Color(1, 0, 0) + Hue(deg=180)).hls
HLS(h=0.5, l=0.5, s=1.0)
Note that whilst multiplication by a :class:`Hue` doesn't make much sense,
it is still supported. However, the circular nature of a hue value can lead
to suprising effects. In particular, since 1.0 is equivalent to 0.0 the
following may be observed::
>>> (Hue(1.0) * Color.from_hls(0.5, 0.5, 1.0)).hls
HLS(h=0.0, l=0.5, s=1.0)
.. _HSL hue wheel: https://en.wikipedia.org/wiki/Hue
"""
def __new__(cls, n=None, deg=None, rad=None):
if n is not None:
return super().__new__(cls, n % 1.0)
elif deg is not None:
return super().__new__(cls, (deg / 360.0) % 1.0)
elif rad is not None:
return super().__new__(cls, (rad / (2 * pi)) % 1.0)
else:
raise ValueError('You must specify a value, or deg or rad')
def __repr__(self):
return "Hue(deg={self.deg:g})".format(self=self)
@property
def deg(self):
"""
Returns the hue as a value in degrees with the range 0.0 <= n < 360.0.
"""
return self * 360.0
@property
def rad(self):
"""
Returns the hue as a value in radians with the range 0.0 <= n < 2π.
"""
return self * 2 * pi
[docs]class Lightness(float):
"""
Represents the lightness of a :class:`Color` for use in transformations.
Instances of this class can be constructed directly with a float value, or
by querying the :attr:`Color.lightness` attribute. Addition, subtraction,
and multiplication are supported with :class:`Color` instances. For
example::
>>> Color(0, 0, 0) + Lightness(0.1)
<Color html='#1a1a1a' rgb=(0.1, 0.1, 0.1)>
>>> Color.from_rgb_bytes(0x80, 0x80, 0) - Lightness(0.2)
<Color html='#1a1a00' rgb=(0.101961, 0.101961, 0)>
>>> Lightness(0.9) * Color('wheat')
<Color html='#f0ce8e' rgb=(0.94145, 0.806785, 0.555021)>
"""
def __repr__(self):
return "Lightness({:g})".format(self)
[docs]class Saturation(float):
"""
Represents the saturation of a :class:`Color` for use in transformations.
Instances of this class can be constructed directly with a float value, or
by querying the :attr:`Color.saturation` attribute. Addition, subtraction,
and multiplication are supported with :class:`Color` instances. For
example::
>>> Color(0.9, 0.9, 0.6) + Saturation(0.1)
<Color html='#ecec93' rgb=(0.925, 0.925, 0.575)>
>>> Color('red') - Saturation(1)
<Color html='#808080' rgb=(0.5, 0.5, 0.5)>
>>> Saturation(0.5) * Color('wheat')
<Color html='#e4d9c3' rgb=(0.896078, 0.85098, 0.766667)>
"""
def __repr__(self):
return "Saturation({:g})".format(self)
[docs]class Luma(float):
"""
Represents the luma of a :class:`Color` for use in transformations.
Instances of this class can be constructed directly with a float value, or
by querying the :attr:`Color.yuv.y` attribute. Addition, subtraction, and
multiplication are supported with :class:`Color` instances. For example::
>>> Color(0, 0, 0) + Luma(0.1)
<Color html='#1a1a1a' rgb=(0.1, 0.1, 0.1)>
>>> Color('red') * Luma(0.5)
<Color html='#d90000' rgb=(0.8505, 0, 0)>
"""
def __repr__(self):
return "Luma({:g})".format(self)