from __future__ import division
from collections import namedtuple
from random import randint, uniform
[docs]class Color(namedtuple('__BaseColor', 'r g b a')):
'''
4-component named tuple: (r, g, b, a), with some methods.
Each component is an unsigned byte in the range 0 to 255. (Note this is
likely to change in the future to using floats throughout. It seems that
once geometry is pushed to a VBO, the performance gains of using ubytes
diminish substantially).
.. function:: __init__(r, g, b[, a=255])
``r``, ``g``, ``b``: red, green and blue
``a``: alpha (defaults to fully opaque)
All of r, g, b, a are ints from 0 to 255 (Color.MAX_CHANNEL.)
For example, to specify red color::
from gloopy.color import Color
red = Color(255, 0, 0)
Or semi-transparent blue::
red = Color(0, 0, 255, 127)
Some predefined instances of Color provide named colors. These named colors
are defined as attributes of the Color class::
from gloopy.color import Color
print Color.RoyalPurple
The names and values are taken from the top 69 results of the xkcd color
survey:
http://blog.xkcd.com/2010/05/03/color-survey-results/
'''
COMPONENTS = 4
MAX_CHANNEL = 0xff
__slots__ = []
# make constructor's 'a' argument optional
def __new__(cls, r, g, b, a=MAX_CHANNEL):
return super(Color, cls).__new__(cls, r, g, b, a)
@staticmethod
[docs] def Random():
'''
Return a new random color
'''
return Color(
randint(0, Color.MAX_CHANNEL),
randint(0, Color.MAX_CHANNEL),
randint(0, Color.MAX_CHANNEL),
)
@staticmethod
[docs] def Randoms():
'''
Generate an infinite sequence of random colors.
'''
while True:
yield Color.Random()
[docs] def as_floats(self):
'''
Returns this color as a tuple of normalised floats, suitable for use
with glSetClearColor. Note this method is likely to be deleted in a
subsequent release, when colors change to being stored internally as
floats.
'''
return (
1 / Color.MAX_CHANNEL * self.r,
1 / Color.MAX_CHANNEL * self.g,
1 / Color.MAX_CHANNEL * self.b,
1 / Color.MAX_CHANNEL * self.a,
)
[docs] def tinted(self, other, bias=0.5):
'''
Return a new color, interpolated between this color and `other` by an
amount specified by `bias`, which normally ranges from 0.0 (entirely
this color) to 1.0 (entirely `other`.)
'''
return Color(
int(self.r * (1 - bias) + other.r * bias),
int(self.g * (1 - bias) + other.g * bias),
int(self.b * (1 - bias) + other.b * bias),
int(self.a * (1 - bias) + other.a * bias),
)
[docs] def variations(self, other=None):
'''
Generate an infinite sequence of colors which are tinted by random
amounts towards `other`, which defaults to a darker version of this
color.
'''
if other is None:
other = self.tinted(Color.Black, 0.5)
while True:
yield self.tinted(other, uniform(0, 1))
[docs] def inverted(self):
'''
Return a new color which is the complement of this one, i.e. if this
color contains a lot of red, the return value will contain little red,
and so on.
'''
return Color(
255 - self.r,
255 - self.g,
255 - self.b,
self.a
)
Color.Purple = Color(0x7e, 0x1e, 0x9c)
Color.Green = Color(0x15, 0xb0, 0x1a)
Color.Blue = Color(0x03, 0x43, 0xdf)
Color.Pink = Color(0xff, 0x81, 0xc0)
Color.Brown = Color(0x65, 0x37, 0x00)
Color.Red = Color(0xe5, 0x00, 0x00)
Color.LightBlue = Color(0x95, 0xd0, 0xfc)
Color.Teal = Color(0x02, 0x93, 0x86)
Color.Orange = Color(0xf9, 0x73, 0x06)
Color.LightGreen = Color(0x96, 0xf9, 0x7b)
Color.Magenta = Color(0xc2, 0x00, 0x78)
Color.Yellow = Color(0xff, 0xff, 0x14)
Color.SkyBlue = Color(0x75, 0xbb, 0xfd)
Color.Grey = Color(0x93, 0x93, 0x93)
Color.LimeGreen = Color(0x89, 0xfe, 0x05)
Color.LightPurple = Color(0xbf, 0x77, 0xf6)
Color.Violet = Color(0x9a, 0x0e, 0xea)
Color.DarkGreen = Color(0x03, 0x35, 0x00)
Color.Turquoise = Color(0x06, 0xc2, 0xac)
Color.Lavender = Color(0xc7, 0x9f, 0xef)
Color.DarkBlue = Color(0x00, 0x03, 0x5b)
Color.Tan = Color(0xd1, 0xb2, 0x6f)
Color.Cyan = Color(0x00, 0xff, 0xff)
Color.Aqua = Color(0x13, 0xea, 0xc9)
Color.ForestGreen = Color(0x06, 0x47, 0x0c)
Color.Mauve = Color(0xae, 0x71, 0x81)
Color.DarkPurple = Color(0x35, 0x06, 0x3e)
Color.BrightGreen = Color(0x01, 0xff, 0x07)
Color.Maroon = Color(0x65, 0x00, 0x21)
Color.Olive = Color(0x6e, 0x75, 0x0e)
Color.Salmon = Color(0xff, 0x79, 0x6c)
Color.Beige = Color(0xe6, 0xda, 0xa6)
Color.RoyalBlue = Color(0x05, 0x04, 0xaa)
Color.NavyBlue = Color(0x00, 0x11, 0x46)
Color.Lilac = Color(0xce, 0xa2, 0xfd)
Color.Black = Color(0x00, 0x00, 0x00)
Color.HotPink = Color(0xff, 0x02, 0x8d)
Color.LightBrown = Color(0xad, 0x81, 0x50)
Color.PaleGreen = Color(0xc7, 0xfd, 0xb5)
Color.Peach = Color(0xff, 0xb0, 0x7c)
Color.OliveGreen = Color(0x67, 0x7a, 0x04)
Color.DarkPink = Color(0xcb, 0x41, 0x6b)
Color.Periwinkle = Color(0x8e, 0x82, 0xfe)
Color.SeaGreen = Color(0x53, 0xfc, 0xa1)
Color.Lime = Color(0xaa, 0xff, 0x32)
Color.Indigo = Color(0x38, 0x02, 0x82)
Color.Mustard = Color(0xce, 0xb3, 0x01)
Color.LightPink = Color(0xff, 0xd1, 0xdf)
Color.Rose = Color(0xcf, 0x62, 0x75)
Color.BrightBlue = Color(0x01, 0x65, 0xfc)
Color.NeonGreen = Color(0x0c, 0xff, 0x0c)
Color.BurntOrange = Color(0xc0, 0x4e, 0x01)
Color.Aquamarine = Color(0x04, 0xd8, 0xb2)
Color.Navy = Color(0x01, 0x15, 0x3e)
Color.GrassGreen = Color(0x3f, 0x9b, 0x0b)
Color.PaleBlue = Color(0xd0, 0xfe, 0xfe)
Color.DarkRed = Color(0x84, 0x00, 0x00)
Color.BrightPurple = Color(0xbe, 0x03, 0xfd)
Color.YellowGreen = Color(0xc0, 0xfb, 0x2d)
Color.BabyBlue = Color(0xa2, 0xcf, 0xfe)
Color.Gold = Color(0xdb, 0xb4, 0x0c)
Color.MintGreen = Color(0x8f, 0xff, 0x9f)
Color.Plum = Color(0x58, 0x0f, 0x41)
Color.RoyalPurple = Color(0x4b, 0x00, 0x6e)
Color.BrickRed = Color(0x8f, 0x14, 0x02)
Color.DarkTeal = Color(0x01, 0x4d, 0x4e)
Color.Burgundy = Color(0x61, 0x00, 0x23)
Color.Khaki = Color(0xaa, 0xa6, 0x62)
Color.BlueGreen = Color(0x13, 0x7e, 0x6d)
# some extras defined by me
Color.White = Color(0xff, 0xff, 0xff)
Color.LightGrey = Color(0xc0, 0xc0, 0xc0)
Color.DarkGrey = Color(0x40, 0x40, 0x40)
Color.All = {
name: value for name, value in Color.__dict__.iteritems()
if isinstance(value, Color)
}