roadsideseb / python-colourlovers

Module to use ColourLovers API
http://python-colourlovers.readthedocs.org
GNU General Public License v3.0
22 stars 7 forks source link

Pb with cl.interp on colors scale #9

Open yoyonel opened 7 years ago

yoyonel commented 7 years ago
In [294]: import colorlover as cl              

In [295]: cl                                   
Out[295]: <module 'colorlover' from '/home/latty/.virtualenvs/screenpulse-unittest/local/lib/python2.7/site-packages/colorlover/__init__.pyc'>                                                

In [296]: colormaps = cl.scales['7']['qual']['Dark2']                                          

In [297]: colormaps                            
Out[297]:                                      
['rgb(27,158,119)',                            
 'rgb(217,95,2)',                              
 'rgb(117,112,179)',                           
 'rgb(231,41,138)',                            
 'rgb(102,166,30)',                            
 'rgb(230,171,2)',                             
 'rgb(166,118,29)']                            

In [298]: cl.interp(colormaps, 11)             
---------------------------------------------------------------------------                    
IndexError                                Traceback (most recent call last)                    
<ipython-input-298-946770f2b787> in <module>() 
----> 1 cl.interp(colormaps, 11)               

/home/latty/.virtualenvs/screenpulse-unittest/local/lib/python2.7/site-packages/colorlover/__init__.pyc in interp(scl, r)                                                                     
   1821         c_i = int(i*math.floor(SCL_FI)/round(r[-1])) # start color index               
   1822         hsl_o = rgb_to_hsl( scl[c_i] ) # convert rgb to hls                            
-> 1823         hsl_f = rgb_to_hsl( scl[c_i+1] )                                               
   1824         section_min = c_i*r[-1]/SCL_FI 
   1825         section_max = (c_i+1)*(r[-1]/SCL_FI)                                           

IndexError: list index out of range            

this bug is not stable, here for example:

In [310]: cl.interp(colormaps, 10)
Out[310]: 
['hsl(162.0, 70.0%, 36.0%)',
 'hsl(70.6666666667, 88.6666666667%, 40.0%)',
 'hsl(72.3333333333, 75.3333333333%, 47.0%)',
 'hsl(167.0, 30.0%, 57.0%)',
 'hsl(275.0, 62.6666666667%, 54.3333333333%)',
 'hsl(248.666666667, 75.6666666667%, 48.0%)',
 'hsl(88.0, 69.0%, 38.0%)',
 'hsl(58.6666666667, 88.3333333333%, 42.6666666667%)',
 'hsl(29.3333333333, 107.666666667%, 47.3333333333%)',
 'hsl(38.0, 70.0%, 38.0%)']

In [311]: cl.interp(colormaps, 11)             
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-311-946770f2b787> in <module>() 
----> 1 cl.interp(colormaps, 11)               

/home/latty/.virtualenvs/screenpulse-unittest/local/lib/python2.7/site-packages/colorlover/__init__.pyc in interp(scl, r)
   1821         c_i = int(i*math.floor(SCL_FI)/round(r[-1])) # start color index
   1822         hsl_o = rgb_to_hsl( scl[c_i] ) # convert rgb to hls
-> 1823         hsl_f = rgb_to_hsl( scl[c_i+1] )
   1824         section_min = c_i*r[-1]/SCL_FI
   1825         section_max = (c_i+1)*(r[-1]/SCL_FI)

IndexError: list index out of range

In [312]: cl.interp(colormaps, 15)
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-312-e3b4fde92ac0> in <module>()
----> 1 cl.interp(colormaps, 15)

/home/latty/.virtualenvs/screenpulse-unittest/local/lib/python2.7/site-packages/colorlover/__init__.pyc in interp(scl, r)
   1821         c_i = int(i*math.floor(SCL_FI)/round(r[-1])) # start color index
   1822         hsl_o = rgb_to_hsl( scl[c_i] ) # convert rgb to hls
-> 1823         hsl_f = rgb_to_hsl( scl[c_i+1] )
   1824         section_min = c_i*r[-1]/SCL_FI
   1825         section_max = (c_i+1)*(r[-1]/SCL_FI)

IndexError: list index out of range

In [313]: cl.interp(colormaps, 16)
Out[313]: 
['hsl(162.0, 70.0%, 36.0%)',
 'hsl(107.2, 81.2%, 38.4%)',
 'hsl(52.4, 92.4%, 40.8%)',
 'hsl(-2.4, 103.6%, 43.2%)',
 'hsl(110.2, 57.2%, 51.0%)',
 'hsl(167.0, 30.0%, 57.0%)',
 'hsl(223.8, 2.8%, 63.0%)',
 'hsl(296.6, 69.2%, 53.8%)',
 'hsl(361.4, 88.8%, 52.2%)',
 'hsl(426.2, 108.4%, 50.6%)',
 'hsl(88.0, 69.0%, 38.0%)',
 'hsl(-8.4, 65.0%, 32.0%)',
 'hsl(-104.8, 61.0%, 26.0%)',
 'hsl(-201.2, 57.0%, 20.0%)',
 'hsl(17.6, 115.4%, 49.2%)',
 'hsl(0.0, 127.0%, 52.0%)']
yoyonel commented 7 years ago

I propose a (new) implementation with scipy/numpy implementation for interp:

def interp(scl, r):
    ''' Interpolate a color scale "scl" to a new one with length "r"
        Fun usage in IPython notebook:
        HTML( to_html( to_hsl( interp( cl.scales['11']['qual']['Paired'], 5000 ) ) ) ) '''
    def rgb_to_hsl(rgb):
        ''' Adapted from M Bostock's RGB to HSL converter in d3.js
            https://github.com/mbostock/d3/blob/master/src/color/rgb.js '''
        r, g, b = float(rgb[0]) / 255.0, \
                  float(rgb[1]) / 255.0, \
                  float(rgb[2]) / 255.0
        mx = max(r, g, b)
        mn = min(r, g, b)
        h = s = l = (mx + mn) / 2
        if mx == mn:  # achromatic
            h = 0
            s = 0 if 0 < l < 1 else h
        else:
            d = mx - mn
            s = d / (mx + mn) if l < 0.5 else d / (2 - mx - mn)
            if mx == r:
                h = (g - b) / d + (6 if g < b else 0)
            elif mx == g:
                h = (b - r) / d + 2
            else:
                h = r - g / d + 4

        return int(round(h * 60, 4)), int(round(s * 100, 4)), int(round(l * 100, 4))

    # convert str RGB to numeric list of tuples
    scl = cl.to_numeric(scl)
    # convert RGB to HSL colormap (allow to interpolate linearly after)
    shsl = map(rgb_to_hsl, scl)
    nb_composants = len(scl[0])
    assert nb_composants == 3
    range_shsl = np.arange(len(shsl))
    interp_range_shsl = np.linspace(0, len(shsl)-1, num=r, endpoint=True)
    scl_interps = [
        interpolate.interp1d(range_shsl, map(itemgetter(composant), shsl))(interp_range_shsl)
        for composant in range(nb_composants)
    ]

    interp_c = map(
        lambda hsl: 'hsl' + str(hsl),
        zip(*scl_interps)
    )

    return cl.to_hsl(interp_c)

I think it's more simple and seems to be more stable (not buggy :p).