vaab / colour

Python color representations manipulation library (RGB, HSL, web, ...)
BSD 2-Clause "Simplified" License
319 stars 41 forks source link

color gradient from one color to black give strange result. #32

Open LBdN opened 7 years ago

LBdN commented 7 years ago

When building a gradient from a color to black, the gradient go through many other hues, instead of going simply darker. It looks quite unnatural. Wouldn't it be possible to treat white and black as special cases ?

vaab commented 7 years ago

Can you check what hue have your start and end color ? Can you reproduce it in python interactive shell and copy paste here for reproduction ?

LBdN commented 7 years ago

ok, I dig a bit and the case is more rare than I thought. It doesn't happen from a normal gradient. It does happen when the end color of the gradient is a very dark version of the start color. I use the following code to transform one into the other :

def light_color(color, percentage, darken ):
    color = Color(color)
    if darken:
        new_lum = ( 1 - percentage/100.0) * color.luminance
    else:
        new_lum = ( 1 + percentage/100.0) * color.luminance
    color.luminance = max(min(new_lum,1), 0)
    return {"color" : [color]}

If the darkening percentage is very high, I get this kind of result :

selection_053

in the console, I have this result :

    Engine    inputs
    Engine      0: [<Color #df1c50>]
    Engine    results
    Engine      color:[<Color black>]
vaab commented 7 years ago

May I share with you a few hints that might help us find the culprit...

On your last message, it is not clear for me if the image you pasted is the result of using .range_to(..) (or color_scale(..) or if it is your code that was used to get this result. In the latter case, this is indeed quite a bit puzzling.

To go forward on this topic, I'll suggest you to:

Many thanks for your interest in colour. If you have time also, you might want to check out this branch that is sketching the future of Colour : #31 ... I still need some reviews that it doesn't break anything.

four43 commented 5 years ago

I just came across this! Sorry if this is an older issue, but I have some code to reproduce:

from colour import Color

start = Color("#da0000")
print("start HSL: " + str(start.hsl))
end = Color("#7a0009")
print("end HSL: " + str(end.hsl))
for color in list(start.range_to(end, 10)):
    print(color.hsl)
start HSL: (0.0, 1.0, 0.42745098039215684)
end HSL: (0.9877049180327868, 1.0, 0.23921568627450981)
(0.0, 1.0, 0.42745098039215684)
(0.10974499089253187, 1.0, 0.4065359477124183)
(0.21948998178506374, 1.0, 0.3856209150326797)
(0.3292349726775956, 1.0, 0.36470588235294116)
(0.4389799635701275, 1.0, 0.3437908496732026)
(0.5487249544626593, 1.0, 0.32287581699346407)
(0.6584699453551912, 1.0, 0.3019607843137255)
(0.7682149362477231, 1.0, 0.28104575163398693)
(0.877959927140255, 1.0, 0.2601307189542484)
(0.9877049180327868, 1.0, 0.23921568627450984)

It's a red to a sort of dark red, but we go through the whole hue range before we end back up a little shifted from where we started. I think it's due to the wrapping at 0/360. Hue isn't linear, it's circle so the color_scale function might need to be more complex to handle that.

This rainbow type effect might be desired behavior for some, I would have liked to take the shortest path however. I could see this being implemented as a flag like force_shortest=True or something as to not break existing functionality but to allow crossing that hue border. image

colour version 0.1.5

LBdN commented 3 years ago

Hi @vaab, I solved the pb on my cython version, so you can try on your end. Note the color instance returned by the delta method is the shortest distance in the color space between self and other. It is not really a valid color, more a delta hence the name. But it serves it purpose correctly. :)

cpdef add(self, Color other):
      cdef float h = self.h + other.h
      if (h < <float>0): h += <float>1
      if (h > <float>1): h -= <float>1
      return Color(
         h = h,
         s = self.s + other.s,
         l = self.l + other.l,
         a = self.a + other.a,
         temp = True
         )
   cpdef delta(self, Color other):
      cdef float d1      = self.h - other.h    
      cdef float d2_sign = 1 if d1 < 0 else -1         
      cdef float d2 = <float>1.0 - abs(d1)
      cdef float d 
      if abs(d2) < abs(d1):
         d = d2_sign * d2
      else:
         d = d1
      return Color(
         h = d,
         s = self.s - other.s,
         l = self.l - other.l,
         a = self.a - other.a,
         temp = True
         )

image