Package Bio :: Package Graphics :: Module ColorSpiral
[hide private]
[frames] | no frames]

Source Code for Module Bio.Graphics.ColorSpiral

  1  """Generate RGB colours suitable for distinguishing categorical data. 
  2   
  3  This module provides a class that implements a spiral 'path' through HSV 
  4  colour space, permitting the selection of a number of points along that path, 
  5  and returning the output in RGB colour space, suitable for use with ReportLab 
  6  and other graphics packages. 
  7   
  8  This approach to colour choice was inspired by Bang Wong's Points of View 
  9  article: Color Coding, in Nature Methods _7_ 573 (doi:10.1038/nmeth0810-573). 
 10   
 11  The module also provides helper functions that return a list for colours, or 
 12  a dictionary of colours (if passed an iterable containing the names of 
 13  categories to be coloured). 
 14  """ 
 15   
 16  # standard library 
 17  import colorsys    # colour format conversions 
 18  from math import log, exp, floor, pi 
 19  import random      # for jitter values 
 20   
 21   
22 -class ColorSpiral(object):
23 """Implement a spiral path through HSV colour space. 24 25 This class provides functions for sampling points along a logarithmic 26 spiral path through HSV colour space. 27 28 The spiral is described by r = a * exp(b * t) where r is the distance 29 from the axis of the HSV cylinder to the current point in the spiral, 30 and t is the angle through which the spiral has turned to reach the 31 current point. a and b are (positive, real) parameters that control the 32 shape of the spiral. 33 34 a: the starting direction of the spiral 35 b: the number of revolutions about the axis made by the spiral 36 37 We permit the spiral to move along the cylinder ('in V-space') between 38 v_init and v_final, to give a gradation in V (essentially, brightness), 39 along the path, where v_init, v_final are in [0,1]. 40 41 A brightness 'jitter' may also be provided as an absolute value in 42 V-space, to aid in distinguishing consecutive colour points on the 43 path. 44 """
45 - def __init__(self, a=1, b=0.33, v_init=0.85, v_final=0.5, 46 jitter=0.05):
47 """Initialise a logarithmic spiral path through HSV colour space 48 49 Arguments: 50 51 o a - Parameter a for the spiral, controls the initial spiral 52 direction. a > 0 53 54 o b - parameter b for the spiral, controls the rate at which the 55 spiral revolves around the axis. b > 0 56 57 o v_init - initial value of V (brightness) for the spiral. 58 v_init in [0,1] 59 60 o v_final - final value of V (brightness) for the spiral 61 v_final in [0,1] 62 63 o jitter - the degree of V (brightness) jitter to add to each 64 selected colour. The amount of jitter will be selected 65 from a uniform random distribution [-jitter, jitter], 66 and V will be maintained in [0,1]. 67 """ 68 # Initialise attributes 69 self.a = a 70 self.b = b 71 self.v_init = v_init 72 self.v_final = v_final 73 self.jitter = jitter
74
75 - def get_colors(self, k, offset=0.1):
76 """Generate k different RBG colours evenly-space on the spiral. 77 78 A generator returning the RGB colour space values for k 79 evenly-spaced points along the defined spiral in HSV space. 80 81 Arguments: 82 83 o k - the number of points to return 84 85 o offset - how far along the spiral path to start. 86 """ 87 # We use the offset to skip a number of similar colours near to HSV axis 88 assert offset > 0 and offset < 1, "offset must be in (0,1)" 89 v_rate = (self._v_final - self._v_init) / float(k) 90 # Generator for colours: we have divided the arc length into sections 91 # of equal length, and step along them 92 for n in range(1, k+1): 93 # For each value of n, t indicates the angle through which the 94 # spiral has turned, to this point 95 t = (1./self._b) * (log(n + (k * offset)) - 96 log((1 + offset) * k * self._a)) 97 # Put 0 <= h <= 2*pi, where h is the angular part of the polar 98 # co-ordinates for this point on the spiral 99 h = t 100 while h < 0: 101 h += 2 * pi 102 h = (h - (floor(h/(2 * pi)) * pi)) 103 # Now put h in [0, 1] for colorsys conversion 104 h = h / (2 * pi) 105 # r is the radial distance of this point from the centre 106 r = self._a * exp(self._b * t) 107 # v is the brightness of this point, linearly interpolated 108 # from self._v_init to self._v_final. Jitter size is sampled from 109 # a uniform distribution 110 if self._jitter: 111 jitter = random.random() * 2 * self._jitter - self._jitter 112 else: 113 jitter = 0 114 v = self._v_init + (n * v_rate + jitter) 115 # We have arranged the arithmetic such that 0 <= r <= 1, so 116 # we can use this value directly as s in HSV 117 yield colorsys.hsv_to_rgb(h, r, max(0, min(v, 1)))
118
119 - def _get_a(self):
120 return self._a
121
122 - def _set_a(self, value):
123 self._a = max(0, value)
124
125 - def _get_b(self):
126 return self._b
127
128 - def _set_b(self, value):
129 self._b = max(0, value)
130
131 - def _get_v_init(self):
132 return self._v_init
133
134 - def _set_v_init(self, value):
135 self._v_init = max(0, min(1, value))
136
137 - def _get_v_final(self):
138 return self._v_final
139
140 - def _set_v_final(self, value):
141 self._v_final = max(0, min(1, value))
142
143 - def _get_jitter(self):
144 return self._jitter
145
146 - def _set_jitter(self, value):
147 self._jitter = max(0, min(1, value))
148 149 a = property(_get_a, _set_a, 150 doc="Parameter controlling initial spiral direction (a > 0)") 151 b = property(_get_b, _set_b, 152 doc="Parameter controlling rate spiral revolves around axis (b > 0)") 153 v_init = property(_get_v_init, _set_v_init, 154 doc="Initial value of V (brightness) for the spiral (range 0 to 1)") 155 v_final = property(_get_v_final, _set_v_final, 156 doc="Final value of V (brightness) for the spiral (range 0 to 1)") 157 jitter = property(_get_jitter, _set_jitter, 158 doc="Degree of V (brightness) jitter to add to each color (range 0 to 1)")
159 160 161 # Convenience functions for those who don't want to bother with a 162 # ColorSpiral object
163 -def get_colors(k, **kwargs):
164 """Returns k colours selected by the ColorSpiral object, as a generator. 165 166 Arguments: 167 168 o k - the number of colours to return 169 170 o **kwargs - pass-through arguments to the ColorSpiral object 171 """ 172 cs = ColorSpiral(**kwargs) 173 return cs.get_colors(k)
174 175
176 -def get_color_dict(l, **kwargs):
177 """Returns a dictionary of colours using the provided values as keys. 178 179 Returns a dictionary, keyed by the members of iterable l, with a 180 colour assigned to each member. 181 182 Arguments: 183 184 o l - an iterable representing classes to be coloured 185 186 o **kwargs - pass-through arguments to the ColorSpiral object 187 """ 188 cs = ColorSpiral(**kwargs) 189 colors = cs.get_colors(len(l)) 190 dict = {} 191 for item in l: 192 dict[item] = colors.next() 193 return dict
194