15

I am trying to visualize some values on a form. They range from 0 to 200 and I would like the ones around 0 be green and turn bright red as they go to 200.

Basically the function should return color based on the value inputted. Any ideas ?

Jon Seigel
  • 12,251
  • 8
  • 58
  • 92
Tomas Pajonk
  • 5,132
  • 8
  • 41
  • 51

8 Answers8

17

Basically, the general method for smooth transition between two values is the following function:

function transition(value, maximum, start_point, end_point):
    return start_point + (end_point - start_point)*value/maximum

That given, you define a function that does the transition for triplets (RGB, HSV etc).

function transition3(value, maximum, (s1, s2, s3), (e1, e2, e3)):
    r1= transition(value, maximum, s1, e1)
    r2= transition(value, maximum, s2, e2)
    r3= transition(value, maximum, s3, e3)
    return (r1, r2, r3)

Assuming you have RGB colours for the s and e triplets, you can use the transition3 function as-is. However, going through the HSV colour space produces more "natural" transitions. So, given the conversion functions (stolen shamelessly from the Python colorsys module and converted to pseudocode :):

function rgb_to_hsv(r, g, b):
    maxc= max(r, g, b)
    minc= min(r, g, b)
    v= maxc
    if minc == maxc then return (0, 0, v)
    diff= maxc - minc
    s= diff / maxc
    rc= (maxc - r) / diff
    gc= (maxc - g) / diff
    bc= (maxc - b) / diff
    if r == maxc then
        h= bc - gc
    else if g == maxc then
        h= 2.0 + rc - bc
    else
        h = 4.0 + gc - rc
    h = (h / 6.0) % 1.0 //comment: this calculates only the fractional part of h/6
    return (h, s, v)

function hsv_to_rgb(h, s, v):
    if s == 0.0 then return (v, v, v)
    i= int(floor(h*6.0)) //comment: floor() should drop the fractional part
    f= (h*6.0) - i
    p= v*(1.0 - s)
    q= v*(1.0 - s*f)
    t= v*(1.0 - s*(1.0 - f))
    if i mod 6 == 0 then return v, t, p
    if i == 1 then return q, v, p
    if i == 2 then return p, v, t
    if i == 3 then return p, q, v
    if i == 4 then return t, p, v
    if i == 5 then return v, p, q
    //comment: 0 <= i <= 6, so we never come here

, you can have code as following:

start_triplet= rgb_to_hsv(0, 255, 0) //comment: green converted to HSV
end_triplet= rgb_to_hsv(255, 0, 0) //comment: accordingly for red

maximum= 200

… //comment: value is defined somewhere here

rgb_triplet_to_display= hsv_to_rgb(transition3(value, maximum, start_triplet, end_triplet))
tzot
  • 92,761
  • 29
  • 141
  • 204
  • 1
    +1; I was going to ask a new question about how to improve a color algorithm i have for coloring a bar chart in HTML/PHP... SO suggested this question as similar and your answer helped me fix the issue without having to ask the question! Thanks! – beggs Jul 30 '09 at 03:49
  • 2
    +1, and a better name for your `transition` function would be `lerp` as that's mathematically what you're doing: http://en.wikipedia.org/wiki/Lerp_(computing) – Keith Apr 22 '10 at 13:52
7
red = (float)val / 200 * 255;

green = (float)(200 - val) / 200 * 255;

blue = 0;

return red << 16 + green << 8 + blue;
Tristan
  • 3,192
  • 3
  • 20
  • 32
Peter Parker
  • 29,093
  • 5
  • 52
  • 80
  • This will make the midpoint (100) puke green: rgb(127, 127, 0). You would want your midpoint to be a bright yellow or orange. See: http://stackoverflow.com/questions/168838/color-scaling-function#168980 – Ates Goral Oct 03 '08 at 21:30
  • 1
    I agree to this, that it will not look nice, however i am a coder and the midpoint was not part of the specification ;) – Peter Parker Oct 03 '08 at 21:36
7

You don't say in what environment you're doing this. If you can work with HSV colors, this would be pretty easy to do by setting S = 100 and V = 100, and determining H by:

H = 0.4 * value + 120

Converting from HSV to RGB is also reasonably easy.

[EDIT] Note: in contrast to some other proposed solutions, this will change color green -> yellow -> orange -> red.

eaolson
  • 14,717
  • 7
  • 43
  • 58
3

Pick a green that you like (RGB1 = #00FF00, e.g.) and a Red that you like (RGB2 = #FF0000, e.g.) and then calculate the color like this

R = R1 * (200-i)/200 + R2 * i/200
G = G1 * (200-i)/200 + G2 * i/200
B = B1 * (200-i)/200 + B2 * i/200
Lou Franco
  • 87,846
  • 14
  • 132
  • 192
2

For best controllable and accurate effect, you should use the HSV color space. With HSV, you can easily scale Hue, Saturation and/or Brightness seperate from each other. Then, you do the transformation to RGB.

ypnos
  • 50,202
  • 14
  • 95
  • 141
2

extending upon @tzot's code... you can also set up a mid-point in between the start and end points, which can be useful if you want a "transition color"!

//comment: s = start_triplet, m = mid_triplet, e = end_triplet
function transition3midpoint = (value, maximum, s, m, e):
    mid = maximum / 2
    if value < mid
      return transition3(value, mid, s, m)
    else
      return transition3(value - mid, mid, m, e)
Sky
  • 4,327
  • 4
  • 26
  • 40
1

Looking through this wikipedia article I personally would pick a path through a color space, and map the values onto that path.

But that's a straight function. I think you might be better suited to a javascript color chooser you can find with a quick color that will give you the Hex, and you can store the Hex.

Tom Ritter
  • 99,986
  • 30
  • 138
  • 174
1

If you use linear ramps for Red and Green values as Peter Parker suggested, the color for value 100 will basically be puke green (127, 127, 0). You ideally want it to be a bright orange or yellow at that midpoint. For that, you can use:

Red = max(value / 100, 1) * 255
Green = (1 - max(value / 100, 1)) * 255
Blue = 0
Ates Goral
  • 137,716
  • 26
  • 137
  • 190