#!/usr/bin/env python # Jacob Joseph # 16 Feb 2010 # color space helper for LED lighting. Primarily to slew between # colors by different schemes import colorsys import math from numpy import arange, array def slew_timebase( steps, mode='linear', atan_inflection=10): """Return an array of step fractions from (0,...,1]. mode is one of 'linear','atan'""" if mode == 'linear': return [ float(i) / steps for i in range(1,steps+1)] elif mode == 'atan': stepsize = 1.0 / steps maxtan = math.atan( 0.5 * atan_inflection) return [ math.atan( a * atan_inflection) / maxtan / 2 + 0.5 for a in arange( -0.5 + stepsize, 0.5 + stepsize, stepsize)] else: assert False, "Unimplemented mode: %s" % mode class ledColor(object): """Store a color. Retain RGB and HSV values, so as to non-destructively change brightness.""" __rgb = None __hsv = None def __init__(self, rgb=None, hsv=None, hsl=None): """Specify rgb, hsl, or hsv as a tuple of 3 floats""" if rgb is not None: self.set_rgb( rgb) #elif hsl is not None: # self.set_hsl(hsl) elif hsv is not None: self.set_hsv(hsv) else: self.set_rgb( (0.1,0.1,0.1)) def __repr__(self): s = "" % ( self.__hsv + self.__rgb) return s def __str__(self): return self.__repr__() def get_rgb(self): return self.__rgb def set_rgb( self, *tup): if len(tup) == 1: tup = tup[0] tup = tuple(tup) assert len(tup) == 3, "Tuple of 3 floats expected. Got '%s'" % tup self.__rgb = tup self.__hsv = colorsys.rgb_to_hsv( *tup) rgb = property(fget=get_rgb, fset=set_rgb) def get_hsv(self): return self.__hsv def set_hsv( self, *tup): if len(tup) == 1: tup = tup[0] tup = tuple(tup) assert len(tup) == 3, "Tuple of 3 floats expected. Got '%s'" % tup self.__hsv = tup self.__rgb = colorsys.hsv_to_rgb( float(tup[0]) / 360, tup[1], tup[2]) hsv = property(fget=get_hsv, fset=set_hsv) def set_value(self, level): """level is 0 (darken completely) to 1 (full itensity)""" self.set_hsv( self.__hsv[0], self.__hsv[1], level) def set_saturation(self, level): """level is 0 (desaturate completely) to 1 (saturate completely)""" self.set_hsv( self.__hsv[0], level, self.__hsv[1]) def set_hue(self, level): self.set_hsv( level, *self.__hsv[1:]) def slew(self, rgb=None, hsv=None, max_steps=100, mode='atan', colorspace='hsv'): """Sweep to a target color, yielding ourself at as many as max_steps intermediates by the shortest distance through a given colorspace. mode is 'linear' or 'atan'.""" if rgb is not None: new = ledColor( rgb = rgb) elif hsv is not None: new = ledColor( hsv = hsv) else: assert False, "Color not specified" steps = max_steps timefactor = slew_timebase( steps, mode) #print "slew", self, rgb, hsv, new if colorspace == 'hsv': cur = self.__hsv diff = array(new.hsv) - cur for f in timefactor: self.set_hsv( f * diff + cur) yield self elif colorspace == 'rgb': cur = self.__rgb diff = array(new.rgb) - cur #print "diff", diff for f in timefactor: self.set_rgb( f * diff + cur) #print "yielding", self yield self else: assert False, "Unimplemented colorspace: %s" % colorspace