views:

220

answers:

3
+1  Q: 

HSL Interpolation

If the hue component of my HSL color is in degrees, how can I correctly interpolate between two hues? Using (h1 + h2) / 2 does not seem to produce desirable results in all cases. Here are two examples that illustrate why:

Let:

red = 0°
yellow = 60°
blue = 240°

(red + yellow) / 2 = 30° (orange)
(yellow + blue) / 2 = 150° (blue green)
(red + blue) / 2 = 120° (green)

As you can see, averaging red and blue produces green instead purple/magenta! However, if we let:

red = 360°
yellow = 60°
blue = 240°

(red + yellow) / 2 = 210° (blue green)
(yellow + blue) / 2 = 150° (blue green)
(red + blue) / 2 = 300° (magenta)

Now, red and blue produce the expected color, but red and yellow produce a blue green color! In what way can I interpolate the hue component of the color so that the result matches what would be expected of mixing actual paint? Thanks!

+1  A: 

You probably didn't want to hear this, but convert to RGB, interpolate and convert back.

edit: just saw "would be expected of mixing actual paint"

In that case you probably want to convert to CMY colour space rather than RGB. Have a look at colour mixing on Wikipedia.

Artelius
HLS is actually a *much* better model for this kind of thing.
RBarryYoung
Oh. Don't listen to me then.
Artelius
+2  A: 

You're basically doing this right to get what I assume you want, which is a color that looks midrange between your two inputs, but you just need to deal with the fact that this calculation is done on a circle which wraps. (I assume that you don't really want "mixing like paint" but just a midrange color -- i.e. you don't want red and green to give brown, right?)

Here's a Python function that will pick the angle between two angles on a circle. It basically just tries the two options (adding 360 or not) and picks the one that gives the angle closest to the original angles:

pairs = [(0, 60), (60, 239), (60, 241), (0, 240), (350, 30)]

def circ_ave(a0, a1):
    r = (a0+a1)/2., ((a0+a1+360)/2.)%360 
    if min(abs(a1-r[0]), abs(a0-r[0])) < min(abs(a0-r[1]), abs(a1-r[1])):
        return r[0]
    else:
        return r[1]

for a0, a1 in pairs:
    print a0, a1, circ_ave(a0, a1)

produces:

0 60    30.0
60 239  149.5  # 60, 240 is ambiguous
60 241  330.5
0 240   300.0
350 30  10.0

(I feel there should be an easier way to do this calculation, but it didn't occur to me.)

tom10
RBarryYoung
+1  A: 

For every color value x, x < 360, it could be x or x+360. You then have 4 pairs of color values. If you find the smallest distance pair, then use the pair to interpolate the color. It might give you the right visual effect.

For example,

red = 0° or 360°
yellow = 60° or 420°
blue = 240° or 600°

the smallest distance of red and yellow is 60(red 0 yellow 60), so the interpolation should be 30.
the smallest distance of yellow and blue is 180(yellow 60 blue 240), so the interpolation should be 150.
the smallest distance of red and blue is 60(red 360 blue 240), so the interpolation should be 300.

leiz