mutable.py (9622B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\chroma\mutable.py 2 from __future__ import division 3 from __future__ import print_function 4 import collections 5 import mathext 6 from chroma.color import Color 7 COLORS = {'aqua': (240, 248, 255), 8 'black': (0, 0, 0), 9 'blue': (0, 0, 255), 10 'fuchsia': (255, 0, 255), 11 'gray': (128, 128, 128), 12 'green': (0, 128, 0), 13 'lime': (0, 255, 0), 14 'maroon': (128, 0, 0), 15 'navy': (0, 0, 128), 16 'olive': (128, 128, 0), 17 'purple': (128, 0, 128), 18 'red': (255, 0, 0), 19 'silver': (192, 192, 192), 20 'teal': (0, 128, 128), 21 'white': (255, 255, 255), 22 'yellow': (255, 255, 0)} 23 24 def _get_rgb_by_name(name): 25 return tuple((x / 255.0 for x in COLORS[name])) 26 27 28 class ColorMutable(collections.Sequence): 29 COMBINED_KWARGS = [('rgb', [('red', 'r'), ('green', 'g'), ('blue', 'b')]), ('rgba', [('red', 'r'), 30 ('green', 'g'), 31 ('blue', 'b'), 32 ('alpha', 'a')]), ('hsl', [('hue', 'h'), ('saturation', 's'), ('lightness', 'l')])] 33 34 def __init__(self, *args, **kwargs): 35 rgb = _get_rgb_by_name('black') 36 a = 1.0 37 if len(args) == 1: 38 args = args[0] 39 if isinstance(args, (ColorMutable, Color)): 40 args = args.rgba 41 elif isinstance(args, (str, unicode)): 42 if args in COLORS: 43 args = _get_rgb_by_name(args.lower()) 44 else: 45 args = hex_to_rgb(args) 46 elif isinstance(args, collections.Sequence): 47 pass 48 else: 49 raise TypeError('ColorMutable value should be a color name, hex value, or a Coloror ColorMutable instance') 50 if len(args) == 3: 51 rgb = tuple(args) 52 elif len(args) == 4: 53 rgb = tuple(args[:3]) 54 a = args[3] 55 elif args: 56 raise ValueError('Invalid number of color components') 57 self.rgb = rgb 58 self.a = a 59 for key, components in self.COMBINED_KWARGS: 60 changed = False 61 if key in kwargs: 62 value = list(kwargs.pop(key)) 63 changed = True 64 else: 65 value = list(getattr(self, key)) 66 for i, names in enumerate(components): 67 if len([ name for name in names if name in kwargs ]) > 1: 68 raise TypeError('Duplicate argument found for {}'.format(names[0])) 69 for name in names: 70 if name in kwargs: 71 value[i] = kwargs.pop(name) 72 changed = True 73 74 if changed: 75 setattr(self, key, value) 76 77 if 'hex' in kwargs: 78 self.hex = kwargs.pop('hex') 79 if kwargs: 80 raise TypeError("'{}' is an invalid keyword argument for this function".format(kwargs.keys()[0])) 81 82 @property 83 def red(self): 84 return self._rgb[0] 85 86 @red.setter 87 def red(self, red): 88 if 0.0 > red or red > 1.0: 89 raise ValueError('Red should be in the range [0, 1]') 90 self._rgb[0] = red 91 92 r = red 93 94 @property 95 def green(self): 96 return self._rgb[1] 97 98 @green.setter 99 def green(self, green): 100 if 0.0 > green or green > 1.0: 101 raise ValueError('Green should be in the range [0, 1]') 102 self._rgb[1] = green 103 104 g = green 105 106 @property 107 def blue(self): 108 return self._rgb[2] 109 110 @blue.setter 111 def blue(self, blue): 112 if 0.0 > blue or blue > 1.0: 113 raise ValueError('Blue should be in the range [0, 1]') 114 self._rgb[2] = blue 115 116 b = blue 117 118 @property 119 def alpha(self): 120 return self._alpha 121 122 @alpha.setter 123 def alpha(self, alpha): 124 if 0.0 > alpha or alpha > 1.0: 125 raise ValueError('Alpha should be in the range [0, 1]') 126 self._alpha = alpha 127 128 a = alpha 129 130 @property 131 def rgb(self): 132 return tuple(self._rgb) 133 134 @rgb.setter 135 def rgb(self, rgb): 136 _validate_rgb(rgb) 137 self._rgb = list(rgb) 138 139 @property 140 def rgba(self): 141 r, g, b = self.rgb 142 return (r, 143 g, 144 b, 145 self.a) 146 147 @rgba.setter 148 def rgba(self, rgba): 149 r, g, b, a = rgba 150 self.rgb = [r, g, b] 151 self.a = a 152 153 @property 154 def hsl(self): 155 return rgb_to_hsl(self.rgb) 156 157 @hsl.setter 158 def hsl(self, hsl): 159 self.rgb = hsl_to_rgb(hsl) 160 161 @property 162 def hue(self): 163 return rgb_to_hsl(self._rgb)[0] 164 165 @hue.setter 166 def hue(self, hue): 167 if 0.0 > hue or hue > 1.0: 168 raise ValueError('Hue should be in the range [0, 1]') 169 _, saturation, lightness = self.hsl 170 self.hsl = (hue, saturation, lightness) 171 172 h = hue 173 174 @property 175 def saturation(self): 176 return rgb_to_hsl(self._rgb)[1] 177 178 @saturation.setter 179 def saturation(self, saturation): 180 print('set s to', saturation) 181 if 0.0 > saturation or saturation > 1.0: 182 raise ValueError('Saturation should be in the range [0, 1]') 183 hue, _, lightness = self.hsl 184 self.hsl = (hue, saturation, lightness) 185 186 s = saturation 187 188 @property 189 def lightness(self): 190 return rgb_to_hsl(self._rgb)[2] 191 192 @lightness.setter 193 def lightness(self, lightness): 194 if 0.0 > lightness or lightness > 1.0: 195 raise ValueError('Lightness should be in the range [0, 1]') 196 hue, saturation, _ = self.hsl 197 self.hsl = (hue, saturation, lightness) 198 199 l = lightness 200 201 @property 202 def hex(self): 203 return _rgb_to_hex(self.rgb) 204 205 @hex.setter 206 def hex(self, hex): 207 self.rgb = hex_to_rgb(hex) 208 209 @property 210 def hex_rgba(self): 211 r, g, b, a = map(lambda x: int(round(x * 255.0)), self.rgba) 212 return u'#{:02x}{:02x}{:02x}{:02x}'.format(r, g, b, a) 213 214 @property 215 def hex_argb(self): 216 r, g, b, a = map(lambda x: int(round(x * 255.0)), self.rgba) 217 return u'#{:02x}{:02x}{:02x}{:02x}'.format(a, r, g, b) 218 219 def __eq__(self, other): 220 if isinstance(other, ColorMutable): 221 return self.hex == other.hex 222 return NotImplemented 223 224 def __ne__(self, other): 225 equal = self.__eq__(other) 226 if equal is NotImplemented: 227 return equal 228 return not equal 229 230 def __str__(self): 231 return self.hex 232 233 def __repr__(self): 234 return '<chroma.ColorMutable {}>'.format(self.hex) 235 236 def __getitem__(self, item): 237 return self.rgba[item] 238 239 def __len__(self): 240 return len(self.rgba) 241 242 243 def _validate_rgb(rgb): 244 r, g, b = rgb 245 for name, value in {'Red': r, 246 'Green': g, 247 'Blue': b}.items(): 248 if value > 1.0 or value < 0.0: 249 raise ValueError('{name} must be between 0 and 1. You provided {value}.'.format(name=name, value=value)) 250 251 252 def rgb_to_hsl(rgb): 253 _validate_rgb(rgb) 254 r, g, b = [ float(value) for value in rgb ] 255 value_min = min(r, g, b) 256 value_max = max(r, g, b) 257 diff = value_max - value_min 258 value_sum = value_min + value_max 259 lightness = value_sum / 2 260 if mathext.is_close(diff, 0.0, abs_tol=1e-07): 261 return (0.0, 0.0, lightness) 262 if lightness < 0.5: 263 saturation = diff / value_sum 264 else: 265 saturation = diff / (2.0 - value_sum) 266 dr = ((value_max - r) / 6 + diff / 2) / diff 267 dg = ((value_max - g) / 6 + diff / 2) / diff 268 db = ((value_max - b) / 6 + diff / 2) / diff 269 if r == value_max: 270 hue = db - dg 271 elif g == value_max: 272 hue = 0.3333333333333333 + dr - db 273 elif b == value_max: 274 hue = 0.6666666666666666 + dg - dr 275 if hue < 0: 276 hue += 1 277 if hue > 1: 278 hue -= 1 279 return (hue, saturation, lightness) 280 281 282 def hsl_to_rgb(hsl): 283 h, s, l = [ float(value) for value in hsl ] 284 for name, value in {'Hue': h, 285 'Saturation': s, 286 'Lightness': l}.items(): 287 if value < 0.0 or 1.0 < value: 288 raise ValueError('{} must be between 0 and 1'.format(name)) 289 290 if mathext.is_close(s, 0.0, abs_tol=1e-07): 291 return (l, l, l) 292 if l < 0.5: 293 v2 = l * (1.0 + s) 294 else: 295 v2 = l + s - s * l 296 v1 = 2.0 * l - v2 297 r = _hue_to_rgb(v1, v2, h + 0.3333333333333333) 298 g = _hue_to_rgb(v1, v2, h) 299 b = _hue_to_rgb(v1, v2, h - 0.3333333333333333) 300 return (r, g, b) 301 302 303 def _hue_to_rgb(v1, v2, vH): 304 while vH < 0: 305 vH += 1 306 307 while vH > 1: 308 vH -= 1 309 310 if 6 * vH < 1: 311 return v1 + (v2 - v1) * 6 * vH 312 if 2 * vH < 1: 313 return v2 314 if 3 * vH < 2: 315 return v1 + (v2 - v1) * (0.6666666666666666 - vH) * 6 316 return v1 317 318 319 def rgb_to_hex(rgb): 320 _validate_rgb(rgb) 321 r, g, b = [ float(value) for value in rgb ] 322 return _rgb_to_hex((r, g, b)) 323 324 325 def _rgb_to_hex(rgb): 326 r, g, b = [ int(round(value * 255)) for value in rgb ] 327 return '#{:02x}{:02x}{:02x}'.format(r, g, b) 328 329 330 def hex_to_rgb(color): 331 try: 332 if not isinstance(color, (str, unicode)): 333 raise ValueError() 334 hex_value = color 335 if color.startswith('#'): 336 hex_value = color[1:] 337 if len(hex_value) == 6: 338 r, g, b = hex_value[0:2], hex_value[2:4], hex_value[4:6] 339 elif len(hex_value) == 3: 340 r, g, b = hex_value[0] * 2, hex_value[1] * 2, hex_value[2] * 2 341 else: 342 raise ValueError() 343 except Exception: 344 raise ValueError('Invalid hex color value provided: {!r}'.format(color)) 345 346 return tuple([ float(int(x, 16)) / 255 for x in (r, g, b) ]) 347 348 349 def hex_to_hsl(color): 350 return rgb_to_hsl(hex_to_rgb(color)) 351 352 353 def hsl_to_hex(hsl): 354 return _rgb_to_hex(hsl_to_rgb(hsl))