Repository: vispy/vispy

Issue:
Add hsv option in ColorArray's constructor
``` python
ColorArray(hsv=mynx3array)
```



Discussion:
how should this interact with the `color` parameter of the constructor?

Looking at the constructor, here's what it's like:

```
    def __init__(self, color='black', alpha=None, clip=False):
```

So, well, if we add another `hsv` parameter, then what happens to the `color` parameter? do we check if both are set and then throw an Exception?

> So, well, if we add another hsv parameter, then what happens to the color parameter? do we check if both are set and then throw an Exception?

That's what I would do indeed. @Eric89GXL WDYT?

So, in that case, the default value of `color='black'` would have to be changed, correct? it would have to be `color=None`.

This is because if someone opts to set the HSV value (`hsv=<value>`), then the default `color` parameter would still be around and would result in an exception. This is clearly wrong.

Is there code that depends on the fact that `color` is black by default?

Might be better to do color=None, check hsv, possibly raise the exception, and then set color='black' if both color and hsv are unset

+1 this would maintain backwards compatibility and extend functionality

Or how about `hsv=True` and `color` remains a colour (just in a different colourspace)?

I like that, or `units='rgb' | 'hsv'`, would allow for other types as well without param explosion

> I like that, or units='rgb' | 'hsv', would allow for other types as well without param explosion

+1, but is `units` the right term? maybe color_space or space or..?

+1 for `color_space`

There are the times I wish python had sum types. `color_space` could have been one :)


Resolving patch:
diff --git a/vispy/color/color_array.py b/vispy/color/color_array.py
--- a/vispy/color/color_array.py
+++ b/vispy/color/color_array.py
@@ -91,6 +91,9 @@ class ColorArray(object):
         If array-like, it must be an Nx3 or Nx4 array-like object.
         Can also be a list of colors, such as
         ``['red', '#00ff00', ColorArray('blue')]``.
+    color_space : 'rgb' | 'hsv'
+       'rgb' (default) : color tuples are interpreted as (r, g, b) components.
+       'hsv' : color tuples are interpreted as (h, s, v) components.
     alpha : float | None
         If no alpha is not supplied in ``color`` entry and ``alpha`` is None,
         then this will default to 1.0 (opaque). If float, it will override
@@ -108,6 +111,9 @@ class ColorArray(object):
         >>> b = ColorArray('#0000ff')  # hex color
         >>> w = ColorArray()  # defaults to black
         >>> w.rgb = r.rgb + g.rgb + b.rgb
+        >>>hsv_color = ColorArray(color_space="hsv", color=(0, 0, 0.5))
+        >>>hsv_color
+        <ColorArray: 1 color ((0.5, 0.5, 0.5, 1.0))>
         >>> w == ColorArray('white')
         True
         >>> w.alpha = 0
@@ -124,9 +130,24 @@ class ColorArray(object):
     Under the hood, this class stores data in RGBA format suitable for use
     on the GPU.
     """
-    def __init__(self, color='black', alpha=None, clip=False):
-        """Parse input type, and set attribute"""
+    def __init__(self, color=None, alpha=None,
+                 clip=False, color_space='rgb'):
+
+        # if color is RGB, then set the default color to black
+        if color_space == 'rgb':
+            # by default, if the color is None, we pick black
+            color = 'black' if color is None else color
+        elif color_space == 'hsv':
+            color = (0, 0, 0) if color is None else color
+            # if the color space is hsv, convert hsv to rgb
+            color = _hsv_to_rgb(color)
+        else:
+            raise ValueError('color_space should be either "rgb" or'
+                             '"hsv", it is ' + color_space)
+
+        # Parse input type, and set attribute"""
         rgba = _user_to_rgba(color, clip=clip)
+
         if alpha is not None:
             rgba[:, 3] = alpha
         self._rgba = None


Test patch:
diff --git a/vispy/color/tests/test_color.py b/vispy/color/tests/test_color.py
--- a/vispy/color/tests/test_color.py
+++ b/vispy/color/tests/test_color.py
@@ -51,6 +51,16 @@ def test_color_array():
     assert_array_equal(x.rgba, .5 * np.ones((3, 4)))
     assert_raises(ValueError, x.__setitem__, (0, 1), 0)
 
+    # test hsv color space colors
+    x = ColorArray(color_space="hsv", color=[(0, 0, 1),
+                   (0, 0, 0.5), (0, 0, 0)])
+    assert_array_equal(x.rgba[0], [1, 1, 1, 1])
+    assert_array_equal(x.rgba[1], [0.5, 0.5, 0.5, 1])
+    assert_array_equal(x.rgba[2], [0, 0, 0, 1])
+
+    x = ColorArray(color_space="hsv")
+    assert_array_equal(x.rgba[0], [0, 0, 0, 1])
+
 
 def test_color_interpretation():
     """Test basic color interpretation API"""