From 1fab881863fb29266a72c7ea39ff0db236d67a3e Mon Sep 17 00:00:00 2001 From: jtm Date: Sun, 11 Mar 2012 14:47:58 +0000 Subject: [PATCH] checkpoint colors --- lesscpy/lessc/color.py | 48 +++++++-- lesscpy/test/css/colors.css | 154 ++++++++++++++++++++++++++ lesscpy/test/css/colors.min.css | 20 ++++ lesscpy/test/less/colors-alpha.less | 20 ++++ lesscpy/test/less/colors.less | 51 ++++++--- lesscpy/test/testcolor.py | 162 ++++++++++++++++++++++++++++ 6 files changed, 430 insertions(+), 25 deletions(-) create mode 100644 lesscpy/test/css/colors.css create mode 100644 lesscpy/test/css/colors.min.css create mode 100644 lesscpy/test/less/colors-alpha.less create mode 100644 lesscpy/test/testcolor.py diff --git a/lesscpy/lessc/color.py b/lesscpy/lessc/color.py index 36bf58b..d954130 100644 --- a/lesscpy/lessc/color.py +++ b/lesscpy/lessc/color.py @@ -100,17 +100,26 @@ class Color(): def hue(self, *args): """ """ - pass + if args: + h, l, s = self._hextohls(args[0]) + return round(h * 360, 3) + raise ValueError('Illegal color values') def saturation(self, *args): """ """ - pass + if args: + h, l, s = self._hextohls(args[0]) + return s * 100 + raise ValueError('Illegal color values') def lightness(self, *args): """ """ - pass + if args: + h, l, s = self._hextohls(args[0]) + return l * 100 + raise ValueError('Illegal color values') def opacity(self, *args): """ @@ -156,6 +165,7 @@ class Color(): def grayscale(self, *args): """ + Simply 100% desaturate. """ if len(args) == 2: return self.desaturate(args[0], 100) @@ -163,6 +173,7 @@ class Color(): def greyscale(self, *args): """ + Wrapper for grayscale """ return self.grayscale(*args) @@ -182,22 +193,43 @@ class Color(): def mix(self, *args): """ - Mix to colors + This algorithm factors in both the user-provided weight + and the difference between the alpha values of the two colors + to decide how to perform the weighted average of the two RGB values. + + It works by first normalizing both parameters to be within [-1, 1], + where 1 indicates "only use color1", -1 indicates "only use color 0", + and all values in between indicated a proportionately weighted average. + + Once we have the normalized variables w and a, + we apply the formula (w + a)/(1 + w*a) + to get the combined weight (in [-1, 1]) of color1. + This formula has two especially nice properties: + + * When either w or a are -1 or 1, the combined weight is also that number + (cases where w * a == -1 are undefined, and handled as a special case). + + * When a is 0, the combined weight is w, and vice versa + + Finally, the weight of color1 is renormalized to be within [0, 1] + and the weight of color2 is given by 1 minus the weight of color1. + Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein http://sass-lang.com """ if len(args) >= 2: try: c1, c2, w = args - if type(w) == str: w = int(w.strip('%')) - w = ((w / 100.0) * 2) - 1 except ValueError: c1, c2 = args - w = 1 + w = 50 + if type(w) == str: w = int(w.strip('%')) + w = ((w / 100.0) * 2) - 1 rgb1 = self._hextorgb(c1) rgb2 = self._hextorgb(c2) a = 0 - w1 = (((w if (w * a) == -1 else (w + a)) / (1 + w * a)) + 1) / 2.0 + w1 = (((w if w * a == -1 else w + a) / (1 + w * a)) + 1) + w1 = w1 / 2.0 w2 = 1 - w1 rgb = [ rgb1[0] * w1 + rgb2[0] * w2, diff --git a/lesscpy/test/css/colors.css b/lesscpy/test/css/colors.css new file mode 100644 index 0000000..8b9742c --- /dev/null +++ b/lesscpy/test/css/colors.css @@ -0,0 +1,154 @@ +#yellow #short { + color: #ffeeaa; +} +#yellow #long { + color: #ffeeaa; +} +#blue #short { + color: #0000ff; +} +#blue #long { + color: #0000ff; +} +#overflow .a { + color: #000000; +} +#overflow .b { + color: #ffffff; +} +#overflow .c { + color: #ffffff; +} +#overflow .d { + color: #00ff00; +} +#grey { + color: #c8c8c8; +} +#808080 { + color: #808080; +} +#00ff00 { + color: #00ff00; +} +.hsl { + color: #ffffff; + color: #ffffff; + color: #000000; + color: #000000; +} +.saturate { + color: #565454; + color: #5e4c4c; + color: #664444; + color: #773333; + color: #882222; + color: #aa0000; + color: #000000; + color: #000000; + color: #ffffff; + color: #ffffff; + color: #29332f; + color: #243830; + color: #203c31; + color: #174533; + color: #0d4f35; + color: #005c37; +} +.desaturate { + color: #555555; + color: #555555; + color: #555555; + color: #555555; + color: #555555; + color: #555555; + color: #000000; + color: #000000; + color: #ffffff; + color: #ffffff; + color: #29332f; + color: #2e2e2e; + color: #2e2e2e; + color: #2e2e2e; + color: #2e2e2e; + color: #2e2e2e; +} +.lighten { + color: #585858; + color: #6e6e6e; + color: #888888; + color: #bbbbbb; + color: #eeeeee; + color: #ffffff; + color: #ffffff; + color: #000000; + color: #ffffff; + color: #ffffff; + color: #2b3632; + color: #404f49; + color: #566c63; + color: #88a096; + color: #c1cdc8; + color: #ffffff; +} +.lighten { + color: #525252; + color: #3b3b3b; + color: #222222; + color: #000000; + color: #000000; + color: #000000; + color: #000000; + color: #000000; + color: #000000; + color: #ffffff; + color: #27302c; + color: #121715; + color: #000000; + color: #000000; + color: #000000; + color: #000000; +} +.spin { + color: #555555; + color: #555555; + color: #555555; + color: #555555; + color: #555555; + color: #555555; + color: #000000; + color: #000000; + color: #ffffff; + color: #ffffff; + color: #29332f; + color: #29332d; + color: #293332; + color: #2a3329; + color: #292d33; + color: #2c2933; +} +.grayscale { + color: #000000; + color: #000000; + color: #ffffff; + color: #ffffff; + color: #2e2e2e; + color: #2e2e2e; + color: #2e2e2e; + color: #2e2e2e; + color: #2e2e2e; + color: #2e2e2e; +} +.mix { + color: #7f007f; + color: #7f7f7f; + color: #7f9055; + color: #3f00bf; + color: #ff0000; + color: #0000ff; +} +.vars { + color: #f6430f; + background-color: #f8b38d; + color: #f1dfda; +} diff --git a/lesscpy/test/css/colors.min.css b/lesscpy/test/css/colors.min.css new file mode 100644 index 0000000..ac70410 --- /dev/null +++ b/lesscpy/test/css/colors.min.css @@ -0,0 +1,20 @@ +#yellow #short{color:#ffeeaa;} +#yellow #long{color:#ffeeaa;} +#blue #short{color:#0000ff;} +#blue #long{color:#0000ff;} +#overflow .a{color:#000000;} +#overflow .b{color:#ffffff;} +#overflow .c{color:#ffffff;} +#overflow .d{color:#00ff00;} +#grey{color:#c8c8c8;} +#808080{color:#808080;} +#00ff00{color:#00ff00;} +.hsl{color:#ffffff;color:#ffffff;color:#000000;color:#000000;} +.saturate{color:#565454;color:#5e4c4c;color:#664444;color:#773333;color:#882222;color:#aa0000;color:#000000;color:#000000;color:#ffffff;color:#ffffff;color:#29332f;color:#243830;color:#203c31;color:#174533;color:#0d4f35;color:#005c37;} +.desaturate{color:#555555;color:#555555;color:#555555;color:#555555;color:#555555;color:#555555;color:#000000;color:#000000;color:#ffffff;color:#ffffff;color:#29332f;color:#2e2e2e;color:#2e2e2e;color:#2e2e2e;color:#2e2e2e;color:#2e2e2e;} +.lighten{color:#585858;color:#6e6e6e;color:#888888;color:#bbbbbb;color:#eeeeee;color:#ffffff;color:#ffffff;color:#000000;color:#ffffff;color:#ffffff;color:#2b3632;color:#404f49;color:#566c63;color:#88a096;color:#c1cdc8;color:#ffffff;} +.lighten{color:#525252;color:#3b3b3b;color:#222222;color:#000000;color:#000000;color:#000000;color:#000000;color:#000000;color:#000000;color:#ffffff;color:#27302c;color:#121715;color:#000000;color:#000000;color:#000000;color:#000000;} +.spin{color:#555555;color:#555555;color:#555555;color:#555555;color:#555555;color:#555555;color:#000000;color:#000000;color:#ffffff;color:#ffffff;color:#29332f;color:#29332d;color:#293332;color:#2a3329;color:#292d33;color:#2c2933;} +.grayscale{color:#000000;color:#000000;color:#ffffff;color:#ffffff;color:#2e2e2e;color:#2e2e2e;color:#2e2e2e;color:#2e2e2e;color:#2e2e2e;color:#2e2e2e;} +.mix{color:#7f007f;color:#7f7f7f;color:#7f9055;color:#3f00bf;color:#ff0000;color:#0000ff;} +.vars{color:#f6430f;background-color:#f8b38d;color:#f1dfda;} diff --git a/lesscpy/test/less/colors-alpha.less b/lesscpy/test/less/colors-alpha.less new file mode 100644 index 0000000..2efae8c --- /dev/null +++ b/lesscpy/test/less/colors-alpha.less @@ -0,0 +1,20 @@ +/* + Color alpha-channel +*/ +#hsla_rgba { + color: hsla(31, 1%, 4%, 60%); + color: hsla(31, 10%, 4%, 60%); + color: hsla(31, 30%, 4%, 60%); + color: hsla(31, 60%, 4%, 60%); + color: hsla(31, 100%, 4%, 60%); + + color: rgba(0, 0, 255, 0.1); + color: rgba(10,10,10,0.6); + color: rgba(11,10,9,0.6); + color: rgba(13,10,7,0.6); + color: rgba(16,10,4,0.6); + color: rgba(20,11,0,0.6); +} +#argb { + color: argb(rgba(0, 0, 255, 0.1)); +} \ No newline at end of file diff --git a/lesscpy/test/less/colors.less b/lesscpy/test/less/colors.less index 6d1b088..f0a7684 100644 --- a/lesscpy/test/less/colors.less +++ b/lesscpy/test/less/colors.less @@ -117,22 +117,22 @@ color: darken(#29332f, 100%); } .spin { - color: spin(#555, 1%); - color: spin(#555, 10%); - color: spin(#555, 20%); - color: spin(#555, 40%); - color: spin(#555, 60%); - color: spin(#555, 100%); - color: spin(#000, 100%); - color: spin(#000, 0%); - color: spin(#fff, 100%); - color: spin(#fff, 0%); - color: spin(#29332f, 1%); - color: spin(#29332f, 10%); - color: spin(#29332f, 20%); - color: spin(#29332f, 40%); - color: spin(#29332f, 60%); - color: spin(#29332f, 100%); + color: spin(#555, 1); + color: spin(#555, -10); + color: spin(#555, 20); + color: spin(#555, -40); + color: spin(#555, 60); + color: spin(#555, -100); + color: spin(#000, 100); + color: spin(#000, 0); + color: spin(#fff, 100); + color: spin(#fff, 0); + color: spin(#29332f, 1); + color: spin(#29332f, -10); + color: spin(#29332f, 20); + color: spin(#29332f, -40); + color: spin(#29332f, 60); + color: spin(#29332f, 100); } .grayscale { color: grayscale(#000, 100%); @@ -146,4 +146,21 @@ color: grayscale(#29332f, 60%); color: grayscale(#29332f, 100%); } - +.mix { + color: mix(#f00, #00f); + color: mix(#f00, #0ff); + color: mix(#f70, #0aa); + color: mix(#f00, #00f, 25%); + color: mix(#f00, #00f, 100%); + color: mix(#f00, #00f, 0%); +} +/* + Variables +*/ +@base: #f04615; +.vars { + color: saturate(@base, 5%); + background-color: lighten(spin(@base, 8), 25%); + @new: hsl(hue(@base), 45%, 90%); + color: @new; +} diff --git a/lesscpy/test/testcolor.py b/lesscpy/test/testcolor.py new file mode 100644 index 0000000..7d9f8a2 --- /dev/null +++ b/lesscpy/test/testcolor.py @@ -0,0 +1,162 @@ +""" + lesscpy tests. +""" +import unittest +if __name__ == '__main__': + import bootstrap +from lesscpy.lessc import color + + +class TestLessColor(unittest.TestCase): + def setUp(self): + self.color = color.Color() + + def test_rgb(self): + test = self.color.rgb + for r, g, b, v in [ + (255,255,255,'#ffffff'), + (100,100,100,'#646464'), + (0,0,0,'#000000'), + ('70%','70%','70%', '#b2b2b2'), + ('1%','1%','1%', '#020202'), + ('100%','100%','100%', '#ffffff'), + ('0%','0%','0%', '#000000'), + ]: + self.assertEqual(test(r, g, b), v) + for args in [ + (255,255,256), + (0,-1,0), + ('100%', '100%', 200), + ('100%', '100%', '200%'), + ]: + self.assertRaises(ValueError, test, args) + + def test_rgba(self): + test = self.color.rgba + for r, g, b, a, v in [ + (255,255,255,255,'#ffffffff'), + (100,100,100,100,'#64646464'), + (0,0,0,0,'#00000000'), + ('70%','70%','70%', '70%', '#b2b2b2b2'), + ('1%','1%','1%', '1%', '#02020202'), + ('100%','100%','100%','100%', '#ffffffff'), + ('0%','0%','0%','0%', '#00000000'), + ]: + self.assertEqual(test(r, g, b, a), v) + for args in [ + (255,255,255,256), + (0,0,0,-1), + ('100%', '100%', '100%', 200), + ('100%', '100%', '100%', '200%'), + ]: + self.assertRaises(ValueError, test, args) + + def test_hsl(self): + """ + """ + test = self.color.hsl + for h, s, l, v in [ + (31, '1%', '4%', '#0a0a0a'), + (0, '100%', '100%', '#ffffff'), + (100, '100%', '100%', '#ffffff'), + (0, '0%', '0%', '#000000'), + (100, '0%', '0%', '#000000'), + ]: + self.assertEqual(test(h, s, l), v) + + def test_hsla(self): + test = self.color.hsla + for h, s, l, a, v in [ + (31, '1%', '4%', '0%', 'rgba(10,10,10,0.0)'), + (31, '30%', '4%', '1%', 'rgba(13,10,7,0.01)'), + (31, '60%', '4%', '20%', 'rgba(16,10,4,0.2)'), + (31, '90%', '4%', '60%', 'rgba(19,11,1,0.6)'), + (31, '100%', '4%', '100%', 'rgba(20,11,0,1.0)'), + ]: + self.assertEqual(test(h, s, l, a), v) + + def test_fmt(self): + test = self.color.fmt + self.assertEqual(test('#000'), '#000000') + self.assertEqual(test('#000000'), '#000000') + self.assertEqual(test('#0000'), '#00000000') + self.assertEqual(test('#00000000'), '#00000000') + self.assertEqual(test('#AAA'), '#aaaaaa') + self.assertEqual(test('#Abc'), '#aabbcc') + self.assertEqual(test('#AbCdEf'), '#abcdef') + self.assertRaises(ValueError, test, '#xxx') + self.assertRaises(ValueError, test, None) + self.assertRaises(ValueError, test, 'aabbcc') + self.assertRaises(ValueError, test, '#4aabbcc') + + def test_saturate(self): + test = self.color.saturate + for c, p, v in [ + ('#555', '1%', '#565454'), + ('#555', '10%', '#5e4c4c'), + ('#555', '20%', '#664444'), + ('#555', '40%', '#773333'), + ('#555', '60%', '#882222'), + ('#555', '100%', '#aa0000'), + ('#000', '100%', '#000000'), + ('#000', '0%', '#000000'), + ('#fff', '100%', '#ffffff'), + ('#fff', '0%', '#ffffff'), + ('#29332f', '1%', '#29332f'), + ('#29332f', '10%', '#243830'), + ('#29332f', '20%', '#203c31'), + ('#29332f', '40%', '#174533'), + ('#29332f', '60%', '#0d4f35'), + ('#29332f', '100%', '#005c37'), + + ]: + self.assertEqual(test(c, p), v, v) + + def test_desaturate(self): + test = self.color.desaturate + for c, p, v in [ + ('#555', '1%', '#555555'), + ('#555', '10%', '#555555'), + ('#555', '20%', '#555555'), + ('#555', '40%', '#555555'), + ('#555', '60%', '#555555'), + ('#555', '100%', '#555555'), + ('#000', '100%', '#000000'), + ('#000', '0%', '#000000'), + ('#fff', '100%', '#ffffff'), + ('#fff', '0%', '#ffffff'), + ('#29332f', '1%', '#29332f'), + ('#29332f', '10%', '#2e2e2e'), + ('#29332f', '20%', '#2e2e2e'), + ('#29332f', '40%', '#2e2e2e'), + ('#29332f', '60%', '#2e2e2e'), + ('#29332f', '100%', '#2e2e2e'), + + ]: + self.assertEqual(test(c, p), v, v) + + def test_spin(self): + test = self.color.spin + for c, p, v in [ + ('#555', '1%', '#555555'), + ('#555', '10%', '#555555'), + ('#555', '20%', '#555555'), + ('#555', '40%', '#555555'), + ('#555', '60%', '#555555'), + ('#555', '100%', '#555555'), + ('#000', '100%', '#000000'), + ('#000', '0%', '#000000'), + ('#fff', '100%', '#ffffff'), + ('#fff', '0%', '#ffffff'), + ('#29332f', '1%', '#29332f'), + ('#29332f', '10%', '#293331'), + ('#29332f', '20%', '#293332'), + ('#29332f', '40%', '#293033'), + ('#29332f', '60%', '#292d33'), + ('#29332f', '100%', '#2c2933'), + + ]: + self.assertEqual(test(c, p), v, v) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file