import math from color import Color class ColorWheel: @property def base_color(self): return self._base_color @property def hue(self): return [ self.base_color, self.add_h(1.0 / 12), self.add_h(2.0 / 12), self.add_h(3.0 / 12), self.add_h(4.0 / 12), self.add_h(5.0 / 12), self.add_h(-6.0 / 12), self.add_h(-5.0 / 12), self.add_h(-4.0 / 12), self.add_h(-3.0 / 12), self.add_h(-2.0 / 12), self.add_h(-1.0 / 12) ] @property def tone(self): return [self.add_white(-2.0 / 16), self.add_white(-1.0 / 16), self.base_color, self.add_white(1.0 / 16), self.add_white(2.0 / 16)] @property def tone15(self): return [self.add_white(-7.0 / 16), self.add_white(-6.0 / 16), self.add_white(-5.0 / 16), self.add_white(-4.0 / 16), self.add_white(-3.0 / 16), self.add_white(-2.0 / 16), self.add_white(-1.0 / 16), self.base_color, self.add_white(1.0 / 16), self.add_white(2.0 / 16), self.add_white(3.0 / 16), self.add_white(4.0 / 16), self.add_white(5.0 / 16), self.add_white(6.0 / 16), self.add_white(7.0 / 16)] @property def complementary_colors(self): return [self.base_color, self.add_h(0.5)] @property def triadic_colors(self): return [self.add_h(-4.0 / 12), self.base_color, self.add_h(4.0 / 12)] @property def split_complementary_colors(self): return [self.add_h(-5.0 / 12), self.base_color, self.add_h(5.0 / 12)] @property def analogous_colors(self): return [self.add_h(-2.0 / 12), self.add_h(-1.0 / 12), self.base_color, self.add_h(1.0 / 12), self.add_h(2.0 / 12)] def __init__(self, c): self._base_color = c self._r = c.r / 255.0 self._g = c.g / 255.0 self._b = c.b / 255.0 @staticmethod def from_hsv(h, s, v): r, g, b = ColorWheel.hsv_to_rgb(h, s, v) c = Color.from_rgb(round(r * 255), round(g * 255), round(b * 255)) return ColorWheel(c) def add_white(self, value): r, g, b = (self._r + value, self._g + value, self._b + value) r = min(max(r, 0.0), 1.0) g = min(max(g, 0.0), 1.0) b = min(max(b, 0.0), 1.0) return self._from_rgb(r, g, b) def add_h(self, value): h, s, v = ColorWheel.rgb_to_hsv(self._r, self._g, self._b) h = (h + value) % 1.0 if h < 0.0: h += 1.0 r, g, b = ColorWheel.hsv_to_rgb(h, s, v) return self._from_rgb(r, g, b) def add_s(self, value): h, s, v = ColorWheel.rgb_to_hsv(self._r, self._g, self._b) s += value s = min(max(s, 0.0), 1.0) r, g, b = ColorWheel.hsv_to_rgb(h, s, v) return self._from_rgb(r, g, b) def add_v(self, value): h, s, v = ColorWheel.rgb_to_hsv(self._r, self._g, self._b) v += value v = min(max(v, 0.0), 1.0) r, g, b = ColorWheel.hsv_to_rgb(h, s, v) return self._from_rgb(r, g, b) @staticmethod def rgb_to_hsv(r, g, b): if r < 0.0 or r > 1.0: raise ValueError() if g < 0.0 or g > 1.0: raise ValueError() if b < 0.0 or b > 1.0: raise ValueError() cmax = max(r, g, b) cmin = min(r, g, b) h = cmax - cmin if h > 0.0: if cmax == r: h = (g - b) / h if h < 0.0: h += 6.0 elif cmax == g: h = 2.0 + (b - r) / h else: h = 4.0 + (r - g) / h h /= 6.0 s = cmax - cmin if cmax != 0.0: s /= cmax v = cmax return h, s, v @staticmethod def hsv_to_rgb(h, s, v): if h < 0.0 or h > 1.0: raise ValueError() if s < 0.0 or s > 1.0: raise ValueError() if v < 0.0 or v > 1.0: raise ValueError() r = v g = v b = v if s > 0.0: h *= 6.0 i = math.floor(h) f = h - i if i == 1: r *= 1 - s * f b *= 1 - s elif i == 2: r *= 1 - s b *= 1 - s * (1 - f) elif i == 3: r *= 1 - s g *= 1 - s * f elif i == 4: r *= 1 - s * (1 - f) g *= 1 - s elif i == 5: g *= 1 - s b *= 1 - s * f else: g *= 1 - s * (1 - f) b *= 1 - s return r, g, b def _from_rgb(self, r, g, b): return Color.from_argb(self.base_color.a, round(r * 255), round(g * 255), round(b * 255))